【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
原创粉丝点击