Java 泛型 super/extends - 不同的行为(用于 OCP java 8 学习目的)
Java Generics super/extends - different behaviors (for OCP java 8 study purposes)
假设我有以下结构:
java.lang.Object
class A extends Object { public boolean mA() {return true;} }
class B extends A { public boolean mB() {return false;} }
现在我正在编写一个主要方法来使用它。
我接受以下概念:('Object' 从 super 的 get 方法返回的类型)
public static void m1(List<? super A> l ) {
l.get(0).toString();
}
因为我可以使用
调用它
m1(new ArrayList<A>());
m1(new ArrayList<Object>());
我也很满意:('A' 从 get 方法返回的扩展类型)
public static void m2(List<? extends A> l ) {
l.get(0).mA();
}
因为我可以使用
调用它
m2(new ArrayList<A>());
m2(new ArrayList<B>());
最后我的问题是:为什么下面带有 'super' 的定义接收类型 'A' 作为参数,而不是 Object?
Predicate<? extends A> p1 = a -> a.mA(); // ok
Predicate<? super A> p2 = a -> a.mA(); // why is it of type 'A' too??
我认为它会变成类似的东西(我知道下面的代码是无效的......我只是认为它会类似于那个)。
PredicateImpl<T super A> {
public boolean test(T t);
}
我也检查了反编译的class,但我没有得到任何新的线索。谁能告诉我引擎盖下发生了什么?我已经阅读了 PECS 讨论,但它在这个具体问题上对我没有帮助。
提前致谢! ;)
===== 收到答案后用额外代码编辑=====
Predicate<? super A> pSuper = a -> a.mA();
Predicate<? extends A> pExtends = a -> a.mA(); // ?? A ??
Predicate<Object> pObject = o -> o.equals(null);
Predicate<A> pA = a -> a.mA();
Predicate<B> pB = b -> b.mB();
pSuper = pObject;
pSuper = pA;
pSuper = pB; // compiler error
pExtends = pObject; // compiler error
pExtends = pA;
pExtends = pB;
有趣的替代语法(尽管我认为 Predicate<? extends Anything> 非常无用,因为它只在测试方法中接受 'null')。不管怎样,我找到了这样的练习,无论如何我都学会了。
Predicate<? extends A> pExtends2 = (B b) -> b.mB();
JLS, Section 9.9讲的是lambda表达式的类型推断。其中一节讨论了存在通配符和边界时的类型推断。
When a generic functional interface is parameterized by wildcards, there are many different instantiations that could satisfy the wildcard and produce different function types. For example, each of Predicate<Integer>
(function type Integer
-> boolean
), Predicate<Number>
(function type Number
-> boolean
), and Predicate<Object>
(function type Object
-> boolean
) is a Predicate<? super Integer>
. Sometimes, it is possible to known from the context, such as the parameter types of a lambda expression, which function type is intended (§15.27.3). Other times, it is necessary to pick one; in these circumstances, the bounds are used.
(大胆强调我的)
此处,对于 Predicate<? super A>
的目标类型,lambda 参数 a
的多种可能类型是可能的:Object
和 A
。目标类型绑定是 ? super A
,因此绑定 A
用作类型。
这对于任何通配符边界(上限或下限)都是最有意义的。有了上限,绑定类型就很有意义了。类型为 A
或更低,但它可能只是 A
,因此 A
方法应该可用。 Predicate<A>
可分配给 Predicate<? extends A>
.
具有下限,类型为 A
或更高。但是为什么不选择Object
呢?因为 A
工作正常。
一个Predicate<A>
可以分配给一个Predicate<? super A>
。 Predicate<Object>
也可以分配给 Predicate<? super A>
,但这会造成不必要的限制。 lambda 表达式将仅限于调用 Object
方法,而它可以像您编写的那样轻松调用 A
方法(方法 mA
)。在这里选择限制性最强的类型是有意义的,这样程序员就可以调用最多的方法。此外,根据边界限制最严格的功能接口类型必须是可分配的。 Predicate<A>
可分配给 Predicate<? super A>
.
您甚至可以将 lambda 表达式 a -> a.mA()
分配给 Predicate<? extends B>
、Predicate<? super B>
甚至 Predicate<B>
。推断出的类型是B
,那个类型肯定有方法mA
,继承自A
.
如果您尝试将 lambda 表达式 a -> a.mA()
分配给 Predicate<Object>
或 Predicate<?>
,则 Object
必须是推断类型。在这里,你会得到关于 mA
not being defined on Object
.
的编译器错误
假设我有以下结构:
java.lang.Object
class A extends Object { public boolean mA() {return true;} }
class B extends A { public boolean mB() {return false;} }
现在我正在编写一个主要方法来使用它。 我接受以下概念:('Object' 从 super 的 get 方法返回的类型)
public static void m1(List<? super A> l ) {
l.get(0).toString();
}
因为我可以使用
调用它m1(new ArrayList<A>());
m1(new ArrayList<Object>());
我也很满意:('A' 从 get 方法返回的扩展类型)
public static void m2(List<? extends A> l ) {
l.get(0).mA();
}
因为我可以使用
调用它m2(new ArrayList<A>());
m2(new ArrayList<B>());
最后我的问题是:为什么下面带有 'super' 的定义接收类型 'A' 作为参数,而不是 Object?
Predicate<? extends A> p1 = a -> a.mA(); // ok
Predicate<? super A> p2 = a -> a.mA(); // why is it of type 'A' too??
我认为它会变成类似的东西(我知道下面的代码是无效的......我只是认为它会类似于那个)。
PredicateImpl<T super A> {
public boolean test(T t);
}
我也检查了反编译的class,但我没有得到任何新的线索。谁能告诉我引擎盖下发生了什么?我已经阅读了 PECS 讨论,但它在这个具体问题上对我没有帮助。
提前致谢! ;)
===== 收到答案后用额外代码编辑=====
Predicate<? super A> pSuper = a -> a.mA();
Predicate<? extends A> pExtends = a -> a.mA(); // ?? A ??
Predicate<Object> pObject = o -> o.equals(null);
Predicate<A> pA = a -> a.mA();
Predicate<B> pB = b -> b.mB();
pSuper = pObject;
pSuper = pA;
pSuper = pB; // compiler error
pExtends = pObject; // compiler error
pExtends = pA;
pExtends = pB;
有趣的替代语法(尽管我认为 Predicate<? extends Anything> 非常无用,因为它只在测试方法中接受 'null')。不管怎样,我找到了这样的练习,无论如何我都学会了。
Predicate<? extends A> pExtends2 = (B b) -> b.mB();
JLS, Section 9.9讲的是lambda表达式的类型推断。其中一节讨论了存在通配符和边界时的类型推断。
When a generic functional interface is parameterized by wildcards, there are many different instantiations that could satisfy the wildcard and produce different function types. For example, each of
Predicate<Integer>
(function typeInteger
->boolean
),Predicate<Number>
(function typeNumber
->boolean
), andPredicate<Object>
(function typeObject
->boolean
) is aPredicate<? super Integer>
. Sometimes, it is possible to known from the context, such as the parameter types of a lambda expression, which function type is intended (§15.27.3). Other times, it is necessary to pick one; in these circumstances, the bounds are used.
(大胆强调我的)
此处,对于 Predicate<? super A>
的目标类型,lambda 参数 a
的多种可能类型是可能的:Object
和 A
。目标类型绑定是 ? super A
,因此绑定 A
用作类型。
这对于任何通配符边界(上限或下限)都是最有意义的。有了上限,绑定类型就很有意义了。类型为 A
或更低,但它可能只是 A
,因此 A
方法应该可用。 Predicate<A>
可分配给 Predicate<? extends A>
.
具有下限,类型为 A
或更高。但是为什么不选择Object
呢?因为 A
工作正常。
一个Predicate<A>
可以分配给一个Predicate<? super A>
。 Predicate<Object>
也可以分配给 Predicate<? super A>
,但这会造成不必要的限制。 lambda 表达式将仅限于调用 Object
方法,而它可以像您编写的那样轻松调用 A
方法(方法 mA
)。在这里选择限制性最强的类型是有意义的,这样程序员就可以调用最多的方法。此外,根据边界限制最严格的功能接口类型必须是可分配的。 Predicate<A>
可分配给 Predicate<? super A>
.
您甚至可以将 lambda 表达式 a -> a.mA()
分配给 Predicate<? extends B>
、Predicate<? super B>
甚至 Predicate<B>
。推断出的类型是B
,那个类型肯定有方法mA
,继承自A
.
如果您尝试将 lambda 表达式 a -> a.mA()
分配给 Predicate<Object>
或 Predicate<?>
,则 Object
必须是推断类型。在这里,你会得到关于 mA
not being defined on Object
.