JAVA范型使用并不“难“,但是却非常重要。充斥着各种框架代码逻辑。内部深层次逻辑可谓十分恶心,甚至被各位大牛批评。
范型类
public class Box<T> {
// T stands for "Type"
private T t;
public void set(T t) { this.t = t; }
public T get() { return t; }
}
Box<Integer> integerBox = new Box<Integer>();
Box<Double> doubleBox = new Box<Double>();
Box<String> stringBox = new Box<String>();
范型方法
public static <K, V> boolean compare(Pair<K, V> p1, Pair<K, V> p2) {
return p1.getKey().equals(p2.getKey()) &&
p1.getValue().equals(p2.getValue());
}
Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.<Integer, String>compare(p1, p2);
上面的调用有没有觉得很麻烦呢?我就调个函数,还要写上函数的范型。在java1.7后,java可以自动帮我们推倒范型值了
Pair<Integer, String> p1 = new Pair<>(1, "apple");
Pair<Integer, String> p2 = new Pair<>(2, "pear");
boolean same = Util.compare(p1, p2);
通配符
在范型的标志性<>括号中,我们不仅仅能找到大写字母,似乎也总能找到一些意外。
//<T extends Comparable<T>>表示T应该至少实现了Comparable接口
public static <T extends Comparable<T>> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e.compareTo(elem) > 0)
++count;
return count;
}
?也是通配符,表示任意类。你可以看见<?>
类型擦除
JAVA只负责在编译期类型进行检查。然后在就丢弃了范型信息。
public class Node<T> {
private T data;
private Node<T> next;
public Node(T data, Node<T> next) {
this.data = data;
this.next = next;
}
public T getData() { return data; }
// ...
}
编译完成后>>>>>>>>>
public class Node {
private Object data;
private Node next;
public Node(Object data, Node next) {
this.data = data;
this.next = next;
}
public Object getData() { return data; }
// ...
}
因为要擦除成Object对象,所以你不能用基本类型使用范型计数。
FAQ
-
T t=new T()可以写吗?
不可以。因为编译器无法知道T的类型,也就找不到对应的字节码文件。 -
Class a = T.class可以写吗?
不可以。编译报错【错误: 无法从类型变量中进行选择】。但是对应语义并非无法实现。
public <T> Class<T> getTClass() {
return (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
}
具体原理请参考本博客的JAVA类型体系。