泛型是Java中一个很是重要的常识点,在Java荟萃类框架中泛型被遍及应用。本文我们将从零开始来看一下Java泛型的设计,将会涉及到通配符处理惩罚,以及让人苦恼的范例擦除。
泛型基本
泛型类
我们首先界说一个简朴的Box类:
public class Box {
private String object;
public void set(String object) { this.object = object; }
public String get() { return object; }
}
这是最常见的做法,这样做的一个弊端是Box内里此刻只能装入String范例的元素,此后假如我们需要装入Integer等其他范例的元素,还必需要别的重写一个Box,代码得不到复用,利用泛型可以很好的办理这个问题。
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类便可以获得复用,我们可以将T替换成任何我们想要的范例:
Box<Integer> integerBox = new Box<Integer>(); Box<Double> doubleBox = new Box<Double>(); Box<String> stringBox = new Box<String>();
泛型要领
看完了泛型类,接下来我们来相识一下泛型要领。声明一个泛型要领很简朴,只要在返回范例前面加上一个雷同<K, V>的形式就行了:
public class Util {
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());
}
}
public class Pair<K, V> {
private K key;
private V value;
public Pair(K key, V value) {
this.key = key;
this.value = value;
}
public void setKey(K key) { this.key = key; }
public void setValue(V value) { this.value = value; }
public K getKey() { return key; }
public V getValue() { return value; }
}
我们可以像下面这样去挪用泛型要领:
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/1.8操作type inference,让Java自动推导出相应的范例参数:
Pair<Integer, String> p1 = new Pair<>(1, "apple"); Pair<Integer, String> p2 = new Pair<>(2, "pear"); boolean same = Util.compare(p1, p2);
界线符
此刻我们要实现这样一个成果,查找一个泛型数组中大于某个特定元素的个数,我们可以这样实现:
public static <T> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray)
if (e > elem) // compiler error
++count;
return count;
}
可是这样很明明是错误的,因为除了short, int, double, long, float, byte, char等原始范例,其他的类并不必然能利用操纵符>,所以编译器报错,那怎么办理这个问题呢?谜底是利用界线符。
public interface Comparable<T> {
public int compareTo(T o);
}
做一个雷同于下面这样的声明,这样就便是汇报编译器范例参数T代表的都是实现了Comparable接口的类,这样便是汇报编译器它们都至少实现了compareTo要领。
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;
}
通配符
在相识通配符之前,我们首先必需要澄清一个观念,照旧借用我们上面界说的Box类,假设我们添加一个这样的要领:
public void boxTest(Box<Number> n) { /* ... */ }
那么此刻Box<Number> n答允接管什么范例的参数?我们是否可以或许传入Box<Integer>可能Box<Double>呢?谜底是否认的,固然Integer和Double是Number的子类,可是在泛型中Box<Integer>可能Box<Double>与Box<Number>之间并没有任何的干系。这一点很是重要,接下来我们通过一个完整的例子来加深一下领略。
首先我们先界说几个简朴的类,下面我们将用到它:
class Fruit {}
class Apple extends Fruit {}
class Orange extends Fruit {}
下面这个例子中,我们建设了一个泛型类Reader,然后在f1()中当我们实验Fruit f = fruitReader.readExact(apples);编译器会报错,因为List<Fruit>与List<Apple>之间并没有任何的干系。
public class GenericReading {
static List<Apple> apples = Arrays.asList(new Apple());
static List<Fruit> fruit = Arrays.asList(new Fruit());
static class Reader<T> {
T readExact(List<T> list) {
return list.get(0);
}
}
static void f1() {
Reader<Fruit> fruitReader = new Reader<Fruit>();
// Errors: List<Fruit> cannot be applied to List<Apple>.
// Fruit f = fruitReader.readExact(apples);
}
public static void main(String[] args) {
f1();
}
}