泛型

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),举例来说:

  1. 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);

不要在返回值上使用通配符

  1. 不会使得 API 带来灵活性
  2. 强制caller方处理通配符
  3. 用户使用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>吗?

posted @ 2025-11-29 10:25  vonlinee  阅读(4)  评论(0)    收藏  举报