总结
- 泛型解决了参数类型缺少检查造成的问题。
- 泛型可以在类、接口、函数上使用。
- 通配符是为了让
Java
泛型支持范围限定,这样使得泛型的灵活性提升,同时也让通用性设计有了更多的空间。
概述
编译期是指把源码交给编译器编译成计算机可执行文件的过程。运行期是指把编译后的文件交给计算机执行,直到程序结束。在Java
中就是把.java
文件编译成.class
文件,再把编译后的文件交给JVM
加载执行。
泛型又叫“参数化类型”。泛型就是在定义类、接口、方法的时候指定某一种特定类型(碗),让类、接口、方法的使用者来决定具体用哪一种类型的参数(盛的东西)。Java
的泛型是在1.5
引入的,只在编译期做泛型检查,运行期泛型就会消失,我们把这称为“泛型擦除”,最终类型都会变成 Object
。
泛型主要解决的问题:
- 集合对元素类型没有任何限制引发的业务问题。
- 把对象写入集合,在获取对象的时候进行强制类型转换出现问题。
语法规则
使用菱形语法表示泛型,例如 List<String> strList= new ArrayList<>();
。
泛型允许在定义类、接口、方法
时使用类型参数,这个类型形参将在变量声明、创建对象、调用方法时动态得指定。
泛型类
类上定义泛型,作用于类的成员变量与函数,代码实例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| public class Apple<T> { private T info;
public T getInfo() { return info; }
public void setInfo(T info) { this.info = info; }
public Apple(T info) { this.info = info; } public static void main(String[] args) { Apple<String> a1 = new Apple<>("苹果"); System.out.printf(a1.getInfo()); Apple<Double> a2 = new Apple<>(2.13); System.out.printf(a2.getInfo()+""); } }
|
泛型接口
接口上定义泛型,作用于函数,代码实例如下:
1 2 3 4 5 6 7 8
| public interface GenericInterface<T> { public T get(); public void set(T t); public T delete(T t); default T defaultFunction(T t){ return t; } }
|
泛型函数
函数返回类型旁加上泛型,作用于函数,代码实例如下:
1 2 3 4 5 6 7 8 9 10
| public class GenericFunction { public <T> void function(T t) { } public <T> T functionTwo(T t) { return t; } public <T> String functionThree(T t) { return ""; } }
|
通配符
通配符是为了让Java
泛型支持范围限定,这样使得泛型的灵活性提升,同时也让通用性设计有了更多的空间。
<?>
:无界通配符,即类型不确定,任意类型
<? extends T>
:上边界通配符,即?
是继承自T
的任意子类型,遵守只读不写
<? super T>
:下边界通配符,即?
是T
的任意父类型,遵守只写不读
「 通配符限定的范围是体现在确认“参数化类型”的时候,而不是“参数化类型”填充后 」
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
|
List<Number> numberList = new ArrayList<Number>();
numberList.add(1); numberList.add(0.5); numberList.add(10000L);
List<Number> numberListTwo = new ArrayList<Integer>();
List<Integer> integerList = new ArrayList<Integer>(); List<Number> numberListThree = integerList;
|
上边界通配符只读不写,下边界通配符只写不读。
<? extends T>
上边界通配符不作为函数入参,只作为函数返回类型,比如List<? extends T>
的使用add函数会编译不通过,get函数则没问题。
<? super T>
下边界通配符不作为函数返回类型,只作为函数入参,比如List<? super T>
的add函数正常调用,get函数也没问题,但只会返回Object。
设计原则可以参考 PECS (producer-extends,consumer-super)原则。PECS原则也就是说,如果参数化类型表示一个生产者E,就使用<? extends E>,如果参数化类型表示一个消费者E,则使用<? super E>。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29
| public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; }public boolean addAll(Collection<? extends E> c) { Object[] a = c.toArray(); int numNew = a.length; ensureCapacityInternal(size + numNew); System.arraycopy(a, 0, elementData, size, numNew); size += numNew; return numNew != 0; }
public void forEach(Consumer<? super E> action) { Objects.requireNonNull(action); final int expectedModCount = modCount; @SuppressWarnings("unchecked") final E[] elementData = (E[]) this.elementData; final int size = this.size; for (int i=0; modCount == expectedModCount && i < size; i++) { action.accept(elementData[i]); } if (modCount != expectedModCount) { throw new ConcurrentModificationException(); } }
|
参考