【java总结】Genneric泛型
来源:互联网 发布:nginx 第三方模块开发 编辑:程序博客网 时间:2024/06/07 11:40
Genneric泛型
在JDK1.5时引进,是 Java 语言中类型安全的一次重要改进。举个例子,在没有泛型的时候,List里可以放入任意Object,有时我们一厢情愿地认为里面所有放的都是String,在使用的时候把内容提取出来都转成String类来用,然而,List里可以有其他类型,如int类型,自定义类等..这种代码在编译期间不会报错,直到运行时才出现问题。泛型的引进,修正了这种问题,一个指定为List<String>的List实现类,只能存放String类型的值,如果往里面塞入其他类型的值,在编译期间就会报错,保证了List<String>类里全是String。当然,java的泛型由于不是在一开始就引进的,所以也并不是那么完美。比如,泛型信息只在编译期间有效,虽然我们定义了像List<String> ,List<Integer>这样不同的List,但并没有真正意义上生成不同的类型,在运行时,它还是原来的最基本的类型,这是一种被称为泛型擦除的现象。什么都不说了,直接看代码吧...
public class Animal {}
public class Dog extends Animal{}
public class ClassTypeTest<T> {Class<T> kind;public ClassTypeTest(Class<T> kind) { this.kind = kind; } public boolean f(Object arg) { return kind.isInstance(arg); } public static void main(String[] args) { ClassTypeTest<Animal> ctt1 = new ClassTypeTest<Animal>(Animal.class); System.out.println(ctt1.f(new Animal())); System.out.println(ctt1.f(new Dog())); ClassTypeTest<Dog> ctt2 = new ClassTypeTest<Dog>(Dog.class); System.out.println(ctt2.f(new Animal())); System.out.println(ctt2.f(new Dog())); } }
/* * 自定义泛型类,泛型方法 */public class GenericClass<T> {private T data;public GenericClass(){}public GenericClass(T data){this.data=data;}public T getData(){return data;}public void setData(T data){this.data=data;}/* * 参数列表的作用:指出你的泛型方法中会用到的参数类型 */ public<Q> void f(T x){ System.out.println(x.getClass().getName()); } /* * 由于在逻辑上GenericClass<Number>不能视为GenericClass<Integer>的父类, * 我们需要一个在逻辑上可以用来表示同时是Box<Integer>和Box<Number>的父类的一个引用类型, * 由此,类型通配符应运而生 * 我们使用使用类型通配符?代替具体的类型实参。此处是类型实参,而不是类型形参! * 且Box<?>在逻辑上是GenericClass<Integer>、GenericClass<Number>... * 等所有GenericClass<具体类型实参>的父类。由此,我们依然可以定义泛型方法,来完成此类需求。 */public static void getData(GenericClass<?> data){System.out.println("data:"+data.getData());}public static void getUpperNumberData(GenericClass<? extends Number> data){System.out.println("data:"+data.getData());}public static void getLowwerNumberData(GenericClass<? super Number> data){System.out.println("data:"+data.getData());}/* * *泛型限定是通过?(通配符)来实现的,表示可以接受任意类型,那?和T(二者单独使用时 * )有啥区别?其实区别也不是很大,仅仅在对参数类型的使用上。 */public void print(ArrayList<?> al){ Iterator<?> it = al.iterator(); while(it.hasNext()) { System.out.println(it.next()); }}public<T> void print2(ArrayList<T> al){ Iterator<T> it = al.iterator(); while(it.hasNext()) { T t = it.next(); //区别就在此处,T可以作为类型来使用,而?仅能作为接收任意类型 System.out.println(t); }}}
/* * 泛型的细节: * 1)、泛型到底代表什么类型取决于调用者传入的类型,如果没传,默认是Object类型; * 2)、使用带泛型的类创建对象时,等式两边指定的泛型必须一致; * 原因:编译器检查对象调用方法时只看变量,然而程序运行期间调用方法时就要考虑对象具体类型了; * 3)、等式两边可以在任意一边使用泛型,在另一边不使用(考虑向后兼容); * 4)、静态方法上的泛型:静态方法无法访问类上定义的泛型。如果静态方法操作的引用数据类型不确定 * 的时候,必须要将泛型定义在方法上。 * 5)、虚拟机虚拟机进行翻译后的原始类型:Object * 6)、<T extends Comparable>会把T类型擦除为Comparable,可以有多个类型限定,擦除为第一个 * 7)、泛型的类型参数必须为类的引用,不能用基本类型(int, short, long, byte, float, * double, char, boolean) * 8)、不能创建参数化类型的数组 * 9)、不能抛出或捕获泛型类的实例 * 10)、类型擦除后有可能出现同名同参数方法 */public class GenericTest {public static void main(String[] args){List list=new ArrayList();list.add("JAVA");list.add("C语言");//list.add(1000);for(int i=0;i<list.size();i++){String name=(String)list.get(i);//1}/* * 为什么 需要泛型?由以上例子可以知道,定义了一个List类型的集合,先加入2个String,随后加入 * 一个Integer类型,这是允许的,因为list默认的类型为Object。在//1中,我们对list每一个 * 值强制转换为String,而这句语句编译阶段正常,在运行阶段却会抛出java.lang.ClassCast * Exception”异常。此类错误编码在编写过程中不易发现。 */ List<String> list2 = new ArrayList<String>(); list2.add("JAVA"); list2.add("C语言"); //list2.add(100); 此句会提示编译错误 for (int i = 0; i < list2.size(); i++) { String name = list2.get(i); // 2 System.out.println("name:" + name); } /* * 采用泛型写法后,想加入一个Integer类型的对象时会出现编译错误, * 通过List<String>,直接限定了list集合中只能含有String类型的元素 * ,从而在//2处无须进行强制类型转换,因为此时,集合能够记住元素的类型信息 * ,编译器已经能够确认它是String类型了。 */ GenericClass<String> name=new GenericClass<String>("Mankind"); GenericClass<Integer> age=new GenericClass<Integer>(19); System.out.println("name class:"+name.getData()); System.out.println("age class:"+age.getData()); System.out.println("name.getClass()==age.getClass():"+ (name.getClass()==age.getClass())); /* * 泛型擦除: * 在使用泛型类时,虽然传入了不同的泛型实参,但并没有真正意义上生成不同的类型, * 传入不同泛型实参的泛型类在内存上只有一个,即还是原来的最基本的类型,这种现象被成为泛型擦除。 * 也就是说,成功编译过后的class文件中是不包含任何泛型信息的。泛型信息不会进入到运行时阶段。 * 泛型类型在逻辑上看以看成是多个不同的类型,实际上都是相同的基本类型。 */ GenericClass<Number> number=new GenericClass<Number>(666); GenericClass.getData(name); GenericClass.getData(age); GenericClass.getData(number); //使用类型通配符?的泛型方法来完成逻辑上父类的需求。 /* * 如果需要定义一个功能类似于getData()的方法,但对类型实参又有进一步的限制: * 只能是Number类及其子类。此时,需要用到类型通配符上限。 */ //GenericClass.getUpperNumberData(name); 此句报错, GenericClass.getUpperNumberData(age); GenericClass.getUpperNumberData(number); /* * 类型通配符上限通过形如<? extends Number>形式定义,相对应的,类型通 * 配符下限为<? super Number>形式,其含义与类型通配符上限正好相反。 */ //GenericClass.getLowwerNumberData(name); 报错 //GenericClass.getLowwerNumberData(age);报错 GenericClass.getLowwerNumberData(number);}}
public class GenericArray<T> {T[] ts; public GenericArray(Class<T> type, int size){ts = (T[]) Array.newInstance(type, size); }public T get(int index) { return ts[index]; } public void set(int index, T t) { ts[index] = t; } public T[] rep() { return ts; }public static void main(String[] args){/* * 不能创建参数化类型的数组,原因如下 * * List<String>[] lsa = new List<String>[10]; // Not really allowed. * Object o = lsa; * Object[] oa = (Object[]) o; * List<Integer> li = new ArrayList<Integer>(); * li.add(new Integer(3)); * oa[1] = li; // Unsound, but passes run time store check * String s = lsa[1].get(0); // Run-time error: ClassCastException. * * 这种情况下,由于JVM泛型的擦除机制,在运行时JVM是不知道泛型信息的,所以可以给oa[1]赋上一个 * ArrayList<Integer>而不会出现ArrayStoreException,但是在取出数据的时候却要做 * 一次类型转换,所以就会出现ClassCastException,如果可以进行泛型数组的声明,上面说的这种 * 情况在编译期将不会出现任何的警告和错误,只有在运行时才会出错。而对泛型数组的声明进行限制,对于 * 这样的情况,可以在编译期提示代码有类型安全问题,比没有任何提示要强很多。 * 基于以上的原因,Java不支持声明泛型数组,更确切地表达是: * 数组的类型不可以是类型变量,除非是采用通配符的方式,看下面这个例子: * * List<?>[] lsa = new List<?>[10]; // OK, array of unbounded wildcard type. * Object o = lsa; * Object[] oa = (Object[]) o; * List<Integer> li = new ArrayList<Integer>(); * li.add(new Integer(3)); * oa[1] = li; // Correct. * String s = (String) lsa[1].get(0); // Run time error, but cast is explicit. * 因为对于通配符的方式,最后取出数据是要做显式的类型转换的,所以并不会存在上一个例子的问题。 */GenericArray<Integer> ga=new GenericArray<Integer>(Integer.class,10);Object[] objs=ga.rep();for(int i=0;i<10;i++){ga.set(i, i);System.out.println(ga.get(i));}try{Integer[] strs = ga.rep(); for(int i=0;i<10;i++){System.out.println(strs[i]);}System.out.println("使用Array.newInstance来创建泛型数组是可以的!!!!! "); }catch(Exception ex){ex.printStackTrace();}}}
/* * 不能在静态域或方法中出现参数类型 * 例如如下错误代码 */public class Test<T>{/* private static T example; //error public static void showExample() //error { action about T... } public static T showExample() //error { action about T.... } * 首先方法是一个返回类型为T的普通方法,而非泛型方法,这和在静态方法中使用非静态参数是一样的, * 静态方法是先于对象而存在内存中的,因此在编译的时候,T的类型无法确定,一定会出现 * “Cannot make a static reference to a non_static reference”这样类似的错误。 */public static <T> void show() { //action }}
0 0
- 【java总结】Genneric泛型
- Java技术总结:Java泛型
- Java总结篇:Java泛型
- 全面总结Java泛型
- 全面总结Java泛型
- 全面总结Java泛型
- 全面总结Java泛型
- 全面总结Java泛型
- Java知识总结-泛型
- 全面总结Java泛型
- 全面总结Java泛型
- 全面总结Java泛型
- 全面总结Java泛型 DEMO
- Java_全面总结Java泛型
- java语法总结07--泛型
- 我的总结:java泛型
- 【总结】Effective java经验之谈,泛型
- java基础总结31-泛型
- 树状数组(二)与poj2155
- memcached源码分析-----item过期失效处理以及LRU爬虫
- STM32单片机工作日记
- IDEA 2016.1 Maven SpringMVC学习教程(一)
- Android------读取并选择系统联系人数据
- 【java总结】Genneric泛型
- app发布到应用市场的有感
- Ubuntu系统中安装RPM格式包的方法
- 机器学习03Logistic回归
- ZigBee基础实验——GPIO输出控制实验-控制Led亮灭
- eclipse查看一个方法被谁引用(调用)的快捷键四种方式
- Struts2 实战:从 登录Demo 看 Struts2 应用开发
- Windows(x86,64bit)升级MySQL 5.7.17免安装版
- 【Dongle】【Web】网上商城中文路径图片加载异常