Java泛型总结

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 list = new ArrayList();

那么在编译阶段

list.add(new Integer(100));

就会出现异常。

泛型使用方法

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

泛型类

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

static class GenericClass {
    T mField;

    GenericClass(T field) {
        mField = field;
    }

    String getDescription() {
        return "Class is " + mField.getClass() + " values is " + mField;
    }
}

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

GenericClass stringGeneric = new GenericClass("ABC");
GenericClass integerGeneric = new GenericClass(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 doubleSelf();
}
接口继承

继承接口有两种方法:

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

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

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

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

接口实现

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

  • 传入实例化类型
class AdvancedComputableInteger implements AdvancedComputableItem {

    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 implements ComputableItem {

    public T doubleSelf() {
        return null;
    }

}

泛型方法

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

public  T createT(Class 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;
}

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

String str = createT(String.class);

泛型通配符

观察下述代码:

/**
 * @param args
 */
public static void main(String[] args) {
    GenericClass numberGeneric = new GenericClass(0);
    GenericClass integerGeneric = new GenericClass(65535);
    printGeneric(numberGeneric);
    printGeneric(integerGeneric);
}

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

static class GenericClass {

    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 mField;

    GenericClass(T field) {
        mField = field;
    }

    String getDescription() {
        return "Class is " + mField.getClass() + " values is " + mField;
    }
}

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

GenericClass integerGeneric = new GenericClass(1);

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

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

泛型擦除

观察下面一组代码:

public static void main(String[] args) {
    // GenericClass stringGeneric = new GenericClass("ABC");
    // GenericClass integerGeneric = new GenericClass(65535);
    // System.out.print("stringGeneric :" + stringGeneric.getDescription() + " \r\n");
    // System.out.print("integerGeneric :" + integerGeneric.getDescription() + " \r\n");
    // String str = createT(String.class);
    // GenericClass numberGeneric = new GenericClass(0);
    // GenericClass integerGeneric = new GenericClass(65535);
    // printGeneric(numberGeneric);
    // printGeneric(integerGeneric);
    // GenericTest gt = new GenericTest();
    // printGeneric(Male.class, gt);
    // printGeneric(Female.class, gt);

    GenericClass stringGeneric = new GenericClass("ABC");
    GenericClass integerGeneric = new GenericClass(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 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."":(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."":(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."":()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[] ls = new ArrayList[10];  

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

List[] ls = new ArrayList[10];  

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

参考文档

java 泛型详解

你可能感兴趣的:(Java泛型总结)