2 回答

TA贡献1876条经验 获得超6个赞
要理解这些问题,您必须了解泛型是如何工作的subtyping(在 Java 中使用关键字明确表示extends)。Andreas 提到了PECS规则,这是它们在 Java 中的表示。
首先,我想指出上面的代码可以通过简单的强制转换来纠正
ArrayList<? super Student> list = new ArrayList<>();
list.add(new Student());
ArrayList<Person> a = (ArrayList<Person>) list; // a covariance
a.add(new Person());
并且编译和运行良好(而不是引发任何异常)
原因很简单,当我们有一个consumer(接受一些对象并使用它们,例如方法add)时,我们希望它接受我们指定类型no more than(超类)的对象T,因为使用过程可能需要任何成员(变量,方法等)它想要的类型,我们希望确保该类型T满足消费者所需的所有成员。
相反,producer为我们生成对象(如get方法)的 a 必须提供no less than指定类型的对象T,以便我们可以访问T生成的对象上的任何成员。
covariance这两个与称为和的子类型形式密切相关contravariance
至于第二个问题,你也可以参考一下的实现Consumer<T>(比较简单):
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
我们需要这个的原因? super T是:当我们使用Consumer方法组合两个 s时andThen,假设前者Consumer采用 type 的对象T,我们希望后者采用 type 的对象no more than T,这样它就不会尝试访问任何T不有。
因此,我们不是简单地写Consumer<T> afterbut Consumer<? super T> after,而是允许前一个消费者(类型T)与一个消费者结合,该消费者接受一个不完全是 type 的对象T,但可能比 小T,方便covariance。这使得以下代码听起来:
Consumer<Student> stu = (student) -> {};
Consumer<Person> per = (person) -> {};
stu.andThen(per);
出于同样的考虑,compose类型方法也适用。Function

TA贡献1824条经验 获得超5个赞
IMO 这可能是 vanilla Java 中最复杂的概念。所以让我们把它分解一下。我将从你的第二个问题开始。
Function<T, R>t获取type 的实例并返回type 的T实例。通过继承,这意味着您可以提供if类型的实例,并类似地返回if类型。rRfooFooFoo extends TbarBarBar extends R
作为一个想要编写一个灵活的泛型方法的库维护者,很难,实际上不可能提前知道所有可能与该方法一起使用的扩展T和R. 那么我们如何编写处理它们的方法呢?此外,这些实例具有扩展基类的类型这一事实与我们无关。
这就是通配符的用武之地。在方法调用期间,我们说您可以使用满足所需类范围的任何类。对于所讨论的方法,我们有两个不同的通配符,它们使用上限和下限泛型类型参数:
public interface Function<T, R>{
default <V> Function<V, R> compose(Function<? super V, ? extends T> before)
现在让我们说我们想利用这个方法......例如让我们定义一些基本类:
class Animal{}
class Dog extends Animal{}
class Fruit{}
class Apple extends Fruit{}
class Fish{}
class Tuna extends Fish{}
想象一下我们的函数和转换定义如下:
Function<Animal, Apple> base = ...;
Function<Fish, Animal> transformation = ...;
我们可以组合这些函数compose来创建一个新函数:
Function<Fish, Apple> composed = base.compose(transformation);
这一切都很好,但现在想象一下,在所需的输出函数中,我们实际上只想用作Tuna输入。如果我们不使用下界作为我们传递给的? super V输入类型参数,那么我们会得到一个编译器错误:Functioncompose
default <V> Function<V, R> compose(Function<V, ? extends T> before)
...
Function<Tuna, Apple> composed = base.compose(transformation);
> Incompatible types:
> Found: Function<Fish, Apple>, required: Function<Tuna, Apple>
发生这种情况是因为调用的返回类型compose指定V为,Tuna而transformation另一方面指定其“ V”为Fish。所以现在当我们尝试传递transformation给compose编译器时需要transformation接受 aTuna作为其V当然Tuna不完全匹配Fish。
另一方面,代码的原始版本 ( ? super V) 允许我们将其视为V下界(即它允许“逆变”与“不变” V)。编译器不会遇到Tuna和之间的不匹配,而是Fish能够成功应用? super V计算结果为 的下限检查Fish super Tuna,自 以来为真Tuna extends Fish。
对于另一种情况,假设我们的调用定义为:
Function<Animal, Apple> base = ...;
Function<Fish, Dog> transformation = ...;
Function<Fish, Apple> composed = base.compose(transformation);
如果我们没有通配符,? extends T那么我们会得到另一个错误:
default <V> Function<V, R> compose(Function<? super V, T> before)
Function<Fish, Apple> composed = base.compose(transformation);
// error converting transformation from
// Function<Fish, Dog> to Function<Fish, Animal>
通配符允许它按照is resolved to 的? extends T方式工作,并且通配符 resolves to可以满足约束条件。TAnimalDogDog extends Animal
对于你的第一个问题;这些边界实际上只在方法调用的上下文中起作用。在该方法的过程中,通配符将被解析为实际类型,就像? super V被解析为Fish和? extends T被解析为一样Dog。如果没有来自泛型签名的信息,我们将无法让编译器知道可以在类型的方法上使用哪个类,因此不允许使用任何类。
添加回答
举报