Java泛型

160 阅读2分钟

范型只在编译阶段有效

ArrayList<String> a = new ArrayList<String>();
ArrayList b = new ArrayList();
Class c1 = a.getClass();
Class c2 = b.getClass();
System.out.println(c1 == c2); // true

输出结果为true,证明了编译之后,程序会采取去泛型化的措施。也就是说Java中的泛型,只在编译阶段有效。

在编译过程中,正确的检验出泛型结果后,会将范型的相关信息擦除。也就是说,成功编译后的class文件中是不包含任何泛型信息的。

ArrayList<String> a = new ArrayList<>();
a.add("CSDN_SEU_Calvin");
Class c = a.getClass();
try {
    Method method = c.getMethod("add", Object.class);
    method.invoke(a, 100);
} catch (Exception e) {
    e.printStackTrace();
}
System.out.println(a); // [CSDN_SEU_Calvin, 100]

上述代码绕过了编译阶段,也绕过了反射。

泛型类和泛型方法

使用泛型:

/*
* 使用泛型
*/
public class FX<T> {
    private T ob;// 定义泛型成员变量

    public FX(T ob) {
        this.ob = ob;
    }

    public T getOb() {
        return ob;
    }

    public void showType() {
        System.out.println("T的实际类型是:" + ob.getClass().getName());
    }

    public static void main(String[] args) {
        FX<Integer> intOb = new FX<Integer>(100);
        intOb.showType();
        System.out.println("value=" + intOb.getOb());
        System.out.println("--------------------------");

        FX<String> strOb = new FX<String>("CSDN_SEU_Calvin");
        strOb.showType();
        System.out.println("value=" + strOb.getOb());
    }
}

// T的实际类型是:java.lang.Integer
// value=100
// --------------------------
// T的实际类型是:java.lang.String
// value=CSDN_SEU_Calvin

不使用泛型:

public class FX {
    private Object ob;

    public FX(Object ob) {
        this.ob = ob;
    }

    public Object getOb() {
        return ob;
    }

    public void showType() {
        System.out.println("T的实际类型是:" + ob.getClass().getName());
    }

    public static void main(String[] args) {
        FX intOb = new FX(100);
        intOb.showType();
        System.out.println("value=" + intOb.getOb());
        System.out.println("--------------------------");

        FX strOb = new FX("CSDN_SEU_Calvin");
        strOb.showType();
        System.out.println("value=" + strOb.getOb());
    }
}
// T的实际类型是:java.lang.Integer
// value=100
// --------------------------
// T的实际类型是:java.lang.String
// value=CSDN_SEU_Calvin

通配符

为了引出通配符的概念,先看以下代码:

List<Integer> ex_int = new ArrayList<>();
List<Number> ext_num = ex_int; // 出现编译错误

出现错误的原因是,虽然Integer是Number的子类,但是List<Integer>不是List<Number>的子类。如果可以,那么也可以使用ext_num.add(newDoubt())在一个List中装入各种不同类型的子类,这显然是不可以的,因为在取出的时候,是无法区分取出的数据是Integer还是Double类型的。

因此,需要一个在逻辑上可以用来同时表示为List<Integer>List<Number>的父类的一个引用类型,因此就出现了类型通配符。在以下例子中表示为FX<?>

public class Genericity {

    public static class FX<T> {
        private T ob;

        public FX(T ob) {
            this.ob = ob;
        }
    }

    public static void main(String[] args) {
        FX<Number> ex_num = new FX<Number>(100);
        FX<Integer> ex_int = new FX<>(200);
        getData(ex_num);
        getData(ex_int);// 使用getData(FX<Number> temp)方法,编译出错

    }

//    public static void getData(FX<Number> temp) {
//        System.out.println("temp的实际类型是:" + temp.ob);
//    }

    public static void getData(FX<?> temp) {
        System.out.println("temp的实际类型是:" + temp.ob);
    }

}

//temp的实际类型是:100
//temp的实际类型是:200

泛型的好处

**类型安全:**通过泛型定义的变量类型限制,编译器可以更有效的提高Java程序的类型安全。

**消除强制类型转换:**使得代码更加可读,并减少出错机会。所有的强制转换都是自动和隐式的。

List和List<?>

  • List实际上也是List<Object>List实际上表示持有任何Object类型的原生List。
  • List<?>表示具有某种特定类型的List,只是我们不知道那种类型是什么。