Java泛型大全


Java泛型大全

文章插图
 
 
引言 
泛型是JAVA中一个非常重要的知识点 , 在Java集合类框架中泛型被广泛应用 。本文我们将从零开始来看一下Java泛型的设计 , 将会涉及到通配符处理 , 以及让人苦恼的类型擦除 。
作者: ZiWenXie http://www.ziwenxie.site/2017/03/01/java-generic/
泛型基础 
泛型类
 
我们首先定义一个简单的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> {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>();泛型方法
 
看完了泛型类 , 接下来我们来了解一下泛型方法 。声明一个泛型方法很简单 , 只要在返回类型前面加上一个类似的形式就行了:
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 = https://www.isolves.com/it/cxkf/yy/JAVA/2019-08-07/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) ++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 n允许接受什么类型的参数?我们是否能够传入Box或者Box呢?答案是否定的 , 虽然Integer和Double是Number的子类 , 但是在泛型中Box或者Box与Box之间并没有任何的关系 。这一点非常重要 , 接下来我们通过一个完整的例子来加深一下理解 。面试官问:Java中的锁有哪些?我跪了……
首先我们先定义几个简单的类 , 下面我们将用到它:
class Fruit {}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() {static class CovariantReader<T> { T readCovariant(List<?extends T> list) { return list.get(0); }}static void f2() { CovariantReader<Fruit> fruitReader = new CovariantReader<Fruit>(); Fruit f = fruitReader.readCovariant(fruit); Fruit a = fruitReader.readCovariant(apples);}public static void main(String[] args) { f2();}


推荐阅读