泛型
Compile time type safety
https://www.reddit.com/r/rust/comments/1ifurb2/i_thought_typescripts_type_system_was_powerful/
编译期和运行时
List<?> list = new ArrayList<>();
// ? 不能写在右边,因为表达式的右边是运行时
List<?> list = new ArrayList<?>();
泛型类型
泛型通配符 wildcards
https://www.youtube.com/watch?v=V1vQf4qyMXg&t=1344s
? 能接收任何类型
泛型类型是不变的(invariant),举例来说:
List<String>和List<Integer>是两种类型,它们都不是List<Object>的子类型
泛型通配符能带来 API 灵活性
List<String> 是List<? extends Object>的子类型
List<Object> 是List<? super String>的子类型
泛型上下界
? extends T
PECS法则 (mnemonic)
Producer extends, Consumer super
See Effective Java 2nd Edition, Item 28:
- For a T producer, use Foo<? extends T>
- For a T consumer, use Foo<? super T>
If your parameter is a producer, it should be <? extends T>, if it's a consumer it has to be <? super T>.
public interface MyCollection<E> {
// src is an E producer
void addAll(Collection<E> src);
// dst is a E consumer
void transferTo(Collection<E> dst);
// src is an E producer
void addAll_Extends(Collection<? extends E> src);
// dst is a E consumer
void transferTo_Super(Collection<? super E> dst);
}
MyCollection<Number> collection = new MyCollectionImpl<>();
// producer example
Collection<Long> longs = new ArrayList<>();
Collection<Number> numbers = new ArrayList<>();
// 错误: 不兼容的类型: Collection<Long>无法转换为Collection<Number>
collection.addAll(longs);
collection.addAll(numbers);
collection.addAll_Extends(longs);
collection.addAll_Extends(numbers);
// consumer
Collection<Object> objects = new ArrayList<>();
Collection<Number> numbers1 = new ArrayList<>();
// 错误: 不兼容的类型: Collection<Object>无法转换为Collection<Number
collection.transferTo(objects);
collection.transferTo(numbers1);
collection.transferTo_Super(objects);
collection.transferTo_Super(numbers1);
另外一个例子:
public static <E> Set<E> union(Set<E> s1, Set<E> s2) {
HashSet<E> union = new HashSet<>(s1);
union.addAll(s2);
return union;
}
s1 和 s2 都是 E producer
Set<Integer> ints = new HashSet<>();
Set<Double> doubles = new HashSet<>();
// 自动推断类型为E
Set<E> union = union(ints, doubles); // error
编译报错
.\GenericDemo1.java:21: error: cannot find symbol
Set<E> union = union(ints, doubles);
^
symbol: class E
location: class GenericDemo1
.\GenericDemo1.java:21: error: method union in class GenericDemo1 cannot be applied to given types;
Set<E> union = union(ints, doubles);
^
required: Set<E>,Set<E>
found: Set<Integer>,Set<Double>
reason: inference variable E has incompatible equality constraints Double,Integer
where E is a type-variable:
E extends Object declared in method <E>union(Set<E>,Set<E>)
2 errors
解决办法
public static <E> Set<E> union(Set<? extends E> s1, Set<? extends E> s2) {
HashSet<E> union = new HashSet<>(s1);
union.addAll(s2);
return union;
}
在的java版本还需要这么写,手动指定泛型类型,否则会有编译报错
Set<Number> union1 = Test.<Number>union(ints, doubles);
Java8 之后,编译不会报错:
Set<Integer> ints = new HashSet<>();
Set<Double> doubles = new HashSet<>();
// 自动推断为 ? extends Number
Set<? extends Number> union = union(ints, doubles);
Set<Number> union1 = union(ints, doubles);
Set<?> union2 = union(ints, doubles);
不要在返回值上使用通配符
- 不会使得 API 带来灵活性
- 强制caller方处理通配符
- 用户使用API时其实是不需要考虑通配符的,增加使用心智负担
static Collection<? extends Number> getNumbers() {
return new ArrayDeque<>();
}
调用方:
Collection<? extends Number> numbers = getNumbers();
// error: provided: Collection<Number> provided: Collection<capture of ? extends Number>
Collection<Number> numbers1 = getNumbers();
Collection<?> numbers2 = getNumbers(); // ok
使用场景
方法参数,可能会变化的场景
如果在类上定义的类型,因为类上定义的类型是确定的,所以此时使用泛型界限意义不大
typesafe heterogenous container pattern
public static Class<?> forName(String className)
Class<?> type = Class.forName("aaa.bbb.C");
以MyBatis的ResultHandler为例
public interface ResultHandler<T> {
void handleResult(ResultContext<? extends T> resultContext);
}
不会主动调用ResultHandler#handleResult方法,因此这里有必要声明ResultContext<? extends T>而不是ResultContext<T>吗?

浙公网安备 33010602011771号