泛型是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(); } }