Java 泛型:"T2 extends T1" 是否意味着 "T1 super T2"?
Java generics: Does "T2 extends T1" imply "T1 super T2"?
如果我有一个泛型 <T2 extends T1>
,编译器会推断出 T1 super T2
吗?
我有一个更复杂的集合,我将其简化为下面的 MWE。该集合应可与具有任何子类型元素的任何此类集合合并。
现在我想知道,为什么在merge
中对forEach
的调用不被接受。它失败了
java.util.function.Consumer<java.util.Optional> cannot be converted to java.util.function.Consumer<java.util.Optional<? super T2>>
我已经在下图中描述了类型关系。在 merge
中,T2
扩展了 T1
。对 forEach
的调用应用于 other
对象,因此 this
的 T1
成为 other.forEach
的 ?
和 [=20 this.merge
的 =] 是 other
的 T1
。因此,this
' T1
应该被接受为 other
的 T1
.
的超级
我也试过 public void merge(Group<? extends T> other)
结果相同。并且 public void merge(Group<T> other)
不接受包含 T1
.
任何子类型元素的此类集合
MWE:
class Group<T1> {
public <T2 extends T1> void merge(Group<T2> other) {
Consumer<Optional<T1>> task = t -> t.ifPresentOrElse(this::foo, this::bar);
other.forEach(task);
}
public Collection<Optional<T1>> values() {
return List.of();
}
public void forEach(Consumer<Optional<? super T1>> consumer) {
values().forEach(consumer);
}
private void foo(T1 t) {}
private void bar() {}
}
关系:
this.T1 -becomes-> other.forEach.?
^ |
| super
extends |
| v
this.merge.T2 -is-> other.T1
如果你有一个 Consumer<Optional<? super T1>> consumer
那么你可以将它传递给(给 accept(T)
方法)一个 Optional<Object>
因为 Object
是 T1
的超类型.
另一方面,您不能将 Optional<Object>
传递给 Consumer<Optional<T1>> task
,因为 T1
可能不是 Object
。
因此您不能将 Consumer<Optional<T1>> task
分配给 Consumer<Optional<? super T1>> consumer
以防止您的消费者传递它不期望的类型的对象。
您可以使用
解决您的问题
class Group<T1> {
public <T2 extends T1> void merge(Group<T2> other) {
Consumer<Optional<? extends T1>> task=t->t.ifPresentOrElse(this::foo, this::bar);
other.forEach(task);
}
public Collection<Optional<T1>> values() {
return List.of();
}
public void forEach(Consumer<? super Optional<T1>> consumer) {
values().forEach(consumer);
}
private void foo(T1 t) {}
private void bar() {}
}
这是 PECS rule 的应用程序。
您的 task
会将 Optional
视为 生产者 ,因此,必须声明 Optional<? extends T1>
以允许可选生产子类型T1
个。我们知道optional会一直充当生产者,但是Java的泛型类型系统没有类总是充当生产者或消费者的概念,这里需要显式的? extends T1
.
forEach
接收到一个消费者,即使是在接口名称中的字面上,所以 ? super …
是声明此消费者可能是 Optional<T1>
超类型的消费者的正确方法。这不仅包括 Object
,Optional
的超类,还包括 Optional<? extends T1>
时 T1 := T2
,如 merge
中的调用,如 Optional<? extends T1>
] 是 Optional<T2>
.
的超类型
当然你也可以直接使用
解决问题
Consumer<Optional<T2>> task = t -> t.ifPresentOrElse(this::foo, this::bar);
因为这只需要 foo
接受一个 T2
参数,它总是这样做,因为 T2
是 T1
的子类型。但是更改 forEach
的签名以提高灵活性始终是一个很好的举措。由于它是一个 public
方法,因此可能会有其他调用者从中受益。
如果我有一个泛型 <T2 extends T1>
,编译器会推断出 T1 super T2
吗?
我有一个更复杂的集合,我将其简化为下面的 MWE。该集合应可与具有任何子类型元素的任何此类集合合并。
现在我想知道,为什么在merge
中对forEach
的调用不被接受。它失败了
java.util.function.Consumer<java.util.Optional> cannot be converted to java.util.function.Consumer<java.util.Optional<? super T2>>
我已经在下图中描述了类型关系。在 merge
中,T2
扩展了 T1
。对 forEach
的调用应用于 other
对象,因此 this
的 T1
成为 other.forEach
的 ?
和 [=20 this.merge
的 =] 是 other
的 T1
。因此,this
' T1
应该被接受为 other
的 T1
.
我也试过 public void merge(Group<? extends T> other)
结果相同。并且 public void merge(Group<T> other)
不接受包含 T1
.
MWE:
class Group<T1> {
public <T2 extends T1> void merge(Group<T2> other) {
Consumer<Optional<T1>> task = t -> t.ifPresentOrElse(this::foo, this::bar);
other.forEach(task);
}
public Collection<Optional<T1>> values() {
return List.of();
}
public void forEach(Consumer<Optional<? super T1>> consumer) {
values().forEach(consumer);
}
private void foo(T1 t) {}
private void bar() {}
}
关系:
this.T1 -becomes-> other.forEach.?
^ |
| super
extends |
| v
this.merge.T2 -is-> other.T1
如果你有一个 Consumer<Optional<? super T1>> consumer
那么你可以将它传递给(给 accept(T)
方法)一个 Optional<Object>
因为 Object
是 T1
的超类型.
另一方面,您不能将 Optional<Object>
传递给 Consumer<Optional<T1>> task
,因为 T1
可能不是 Object
。
因此您不能将 Consumer<Optional<T1>> task
分配给 Consumer<Optional<? super T1>> consumer
以防止您的消费者传递它不期望的类型的对象。
您可以使用
解决您的问题class Group<T1> {
public <T2 extends T1> void merge(Group<T2> other) {
Consumer<Optional<? extends T1>> task=t->t.ifPresentOrElse(this::foo, this::bar);
other.forEach(task);
}
public Collection<Optional<T1>> values() {
return List.of();
}
public void forEach(Consumer<? super Optional<T1>> consumer) {
values().forEach(consumer);
}
private void foo(T1 t) {}
private void bar() {}
}
这是 PECS rule 的应用程序。
您的 task
会将 Optional
视为 生产者 ,因此,必须声明 Optional<? extends T1>
以允许可选生产子类型T1
个。我们知道optional会一直充当生产者,但是Java的泛型类型系统没有类总是充当生产者或消费者的概念,这里需要显式的? extends T1
.
forEach
接收到一个消费者,即使是在接口名称中的字面上,所以 ? super …
是声明此消费者可能是 Optional<T1>
超类型的消费者的正确方法。这不仅包括 Object
,Optional
的超类,还包括 Optional<? extends T1>
时 T1 := T2
,如 merge
中的调用,如 Optional<? extends T1>
] 是 Optional<T2>
.
当然你也可以直接使用
解决问题Consumer<Optional<T2>> task = t -> t.ifPresentOrElse(this::foo, this::bar);
因为这只需要 foo
接受一个 T2
参数,它总是这样做,因为 T2
是 T1
的子类型。但是更改 forEach
的签名以提高灵活性始终是一个很好的举措。由于它是一个 public
方法,因此可能会有其他调用者从中受益。