美文网首页
Java泛型总结

Java泛型总结

作者: ObadiObada | 来源:发表于2017-12-01 15:24 被阅读0次

    Java泛型总结

    概述

    泛型JDK1.5之引入,其含义是 “参数化类型”。即在定义时将具体的类型参数化,在使用时再将具体的类型传入,即数据的具体的类型是一个参数,在真正使用时传入实际的类型。例如:

    void testNonGenericArrayList() {
        List list = new ArrayList();
        list.add("ABC");
        list.add(new Integer(100));
    
        for (Object o : list) {
            String s = (String) o;
            System.out.print("Object is " + s);
        }
    }
    

    上述的代码回出现 ClassCastException。因为Object无法转换成String类型。如果将List改为使用泛型定义

    List<String> list = new ArrayList<String>();
    

    那么在编译阶段

    list.add(new Integer(100));
    

    就会出现异常。

    泛型使用方法

    泛型可以作用在类,接口,方法中,使用了泛型的类,接口,方法就被称为泛型类,泛型接口,泛型方法。

    泛型类

    通过泛型类,可以抽象对一系列类型的通用操作,一个泛型类的基本写法为:

    static class GenericClass<T> {
        T mField;
    
        GenericClass(T field) {
            mField = field;
        }
    
        String getDescription() {
            return "Class is " + mField.getClass() + " values is " + mField;
        }
    }
    

    其中<T>即为参数化类型,使用时中需要以真正的类型代替。使用如下:

    GenericClass<String> stringGeneric = new GenericClass<String>("ABC");
    GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
    System.out.print("stringGeneric :" + stringGeneric.getDescription() + " \r\n");
    System.out.print("integerGeneric :" + integerGeneric.getDescription() + " \r\n");
    

    输出结果:

    stringGeneric :Class is class java.lang.String values is ABC

    integerGeneric :Class is class java.lang.Integer values is 65535

    从代码看,这个泛型类抽象了“打印承运类名称和值的操作”。

    泛型接口

    泛型接口的定义方法和泛型类类似

    interface ComputableItem<T> {
        T doubleSelf();
    }
    
    接口继承

    继承接口有两种方法:

    • 不传人参数化类型
    interface AdvancedComputableItem<T> extends ComputableItem<T> {
        int tripeSelf();
    }
    

    这种情况下子接口必须包含和父接口一致的参数化类型。

    • 传入参数化类型
    interface ComputableString extends ComputableItem<String> {
        String tripeSelf();
    }
    

    这种情况下子接口无需包含参数化类型。

    接口实现

    接口的实现类有下面三种形式:

    • 传入实例化类型
    class AdvancedComputableInteger implements AdvancedComputableItem<Integer> {
    
        Integer mField;
    
        public Integer doubleSelf() {
            return mField * 2;
        }
    
        public Integer tripeSelf() {
            return mField * 3;
        }
    
    }
    
    • 继承已经传入了实例化类型的子类
    
    class ConnectableString implements ComputableString {
    
        String mField;
    
        public String doubleSelf() {
            return mField + " : " + mField;
        }
    
        public int tripeSelf() {
            return mField.hashCode() % 5 * 3;
        }
    
    }
    
    • 通过泛型类实现
    class NullComputableItem<T> implements ComputableItem<T> {
    
        public T doubleSelf() {
            return null;
        }
    
    }
    

    泛型方法

    泛型方法是泛型中使用较为复杂的,一个例子如下:

    public <T> T createT(Class<T> tClass) {
        T t = null;
        try {
            t = tClass.newInstance();
        } catch (InstantiationException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return t;
    }
    

    其中<T>表示结构化参数, 使用方法如下:

    String str = createT(String.class);
    

    泛型通配符

    观察下述代码:

    /**
     * @param args
     */
    public static void main(String[] args) {
        GenericClass<Number> numberGeneric = new GenericClass<Number>(0);
        GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
        printGeneric(numberGeneric);
        printGeneric(integerGeneric);
    }
    
    static void printGeneric(GenericClass<Number> number) {
        System.out.print("printGeneric :" + number.getDescription() + " \r\n");
    }
    
    static class GenericClass<T> {
    
        T mField;
    
        GenericClass(T field) {
            mField = field;
        }
    
        String getDescription() {
            return "Class is " + mField.getClass() + " values is " + mField;
        }
    }
    

    虽然IntegerNumber的子类,但是编译器仍然会提示printGeneric(integerGeneric)错误。因为IntegerNumber都只是类型参数而已。JDK提供了一种叫做类型统配符的方法来处理这种情况:

    static void printGeneric(GenericClass<?> number) {
        System.out.print("printGeneric :" + number.getDescription() + " \r\n");
    }
    

    这里的?表示通配符,即类型实参。

    泛型边界

    假设有三个类,其中People是父类,Male和Female是子类,定义泛型类时,可以限定类型参数的范围

    static class People {
    
        protected String dress() {
            return "Dress cloth";
        }
    
        @Override
        public String toString() {
            return dress();
        }
    }
    
    static class Male extends People {
    
        @Override
        protected String dress() {
            return "Dress Suit";
        }
    }
    
    static class Female extends People {
    
        @Override
        protected String dress() {
            return "Dress Skirt";
        }
    }
    
    static class GenericClass<T extends People> {
    
        T mField;
    
        GenericClass(T field) {
            mField = field;
        }
    
        String getDescription() {
            return "Class is " + mField.getClass() + " values is " + mField;
        }
    }
    

    将类型参数限定后,编译器会对不符合限定的类型实参报错,例如下面这种写法:

    GenericClass<Integer> integerGeneric = new GenericClass<Integer>(1);
    

    同样泛型的编辑也可以作用于泛型方法:

    static void printGeneric(GenericClass<?> item) {
        System.out.print("printGeneric :" + item.getDescription() + " \r\n");
    }
    

    泛型擦除

    观察下面一组代码:

    public static void main(String[] args) {
        // GenericClass<String> stringGeneric = new GenericClass<String>("ABC");
        // GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
        // System.out.print("stringGeneric :" + stringGeneric.getDescription() + " \r\n");
        // System.out.print("integerGeneric :" + integerGeneric.getDescription() + " \r\n");
        // String str = createT(String.class);
        // GenericClass<Number> numberGeneric = new GenericClass<Number>(0);
        // GenericClass<Integer> integerGeneric = new GenericClass<Integer>(65535);
        // printGeneric(numberGeneric);
        // printGeneric(integerGeneric);
        // GenericTest gt = new GenericTest();
        // printGeneric(Male.class, gt);
        // printGeneric(Female.class, gt);
    
        GenericClass<String> stringGeneric = new GenericClass<String>("ABC");
        GenericClass<Integer> integerGeneric = new GenericClass<Integer>(100);
        System.out.print("String :" + stringGeneric.getClass() + " \r\n");
        System.out.print("Integer:" + integerGeneric.getClass() + " \r\n");
        String str = stringGeneric.get();
        Integer integer = integerGeneric.get();
        System.out.print("str:" + str + " integer:" + integer);
    
    }
    
    static class GenericClass<T> {
        T mField;
    
        GenericClass(T field) {
            mField = field;
        }
    
        T get() {
            return mField;
        }
    
        String getDescription() {
            return "Class is " + mField.getClass() + " values is " + mField;
        }
    
    }
    

    输出结果为:

    String class GenericTest$GenericClass

    Integer class GenericTest$GenericClass

    str:ABC integer:100

    从输出结果看无论类型参数如何传输,泛型的类都是一样的,我们从字节码来观察下,main函数字节码如下:

     public static void main(java.lang.String[]);
        Code:
           0: new           #2                  // class GenericTest$GenericClass
           3: dup           
           4: ldc           #3                  // String ABC
           6: invokespecial #4                  // Method GenericTest$GenericClass."<init>":(Ljava/lang/Object;)V
           9: astore_1      
          10: new           #2                  // class GenericTest$GenericClass
          13: dup           
          14: bipush        100
          16: invokestatic  #5                  // Method java/lang/Integer.valueOf:(I)Ljava/lang/Integer;
          19: invokespecial #4                  // Method GenericTest$GenericClass."<init>":(Ljava/lang/Object;)V
          22: astore_2      
          23: aload_1       
          24: invokevirtual #6                  // Method GenericTest$GenericClass.get:()Ljava/lang/Object;
          27: checkcast     #7                  // class java/lang/String
          30: astore_3      
          31: aload_2       
          32: invokevirtual #6                  // Method GenericTest$GenericClass.get:()Ljava/lang/Object;
          35: checkcast     #8                  // class java/lang/Integer
          38: astore        4
          40: getstatic     #9                  // Field java/lang/System.out:Ljava/io/PrintStream;
          43: new           #10                 // class java/lang/StringBuilder
          46: dup           
          47: invokespecial #11                 // Method java/lang/StringBuilder."<init>":()V
          50: ldc           #12                 // String str:
          52: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          55: aload_3       
          56: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          59: ldc           #14                 // String  integer:
          61: invokevirtual #13                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
          64: aload         4
          66: invokevirtual #15                 // Method java/lang/StringBuilder.append:(Ljava/lang/Object;)Ljava/lang/StringBuilder;
          69: invokevirtual #16                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
          72: invokevirtual #17                 // Method java/io/PrintStream.print:(Ljava/lang/String;)V
          75: return 
    

    可以看到 24行调用get方法返回类型为Object,第27行将Object转换成String。同样32行和35行也有类似的操作。

    泛型数组

    Java中不允许声明泛型数组,即下述写法是非法的:

    List<String>[] ls = new ArrayList<String>[10];  
    

    但是可以通过通配符声明:

    List<?>[] ls = new ArrayList<?>[10];  
    

    具体的原因Sun官网的一篇文章以及本文的参考文档上都有有介绍

    参考文档

    java 泛型详解

    相关文章

      网友评论

          本文标题:Java泛型总结

          本文链接:https://www.haomeiwen.com/subject/lkrwbxtx.html