结合 <?扩展 ClassE> 和 <?超B类>
Combining <? extends ClassE> and <? super ClassB>
我怎么理解的?延伸..和? super .. 自己工作,哪些泛型类型是可能的,但我只是不明白这个层次结构是如何可能的:
-> 表示扩展
类分别是X(最低),A到E(最高)
接口为F
X -> A(实现 F)-> B -> C -> E(实现 F)
还有 D -> E
public class Node<T extends ClassE> {
private T info;
public T getInfo() {
return info;
}
public void setInfo(T info) {
this.info = info;
}
}
public static void main (String [] args){
Node<? super ClassB> n2 = new Node<ClassC>();
// this makes sense, since Node accepts below E and above B
InterfaceF i2 = n2.getInfo();
// how? Not only outside of <? extends E> but also getting value even though
// <? super B> is defined above, what's up with PECS?
n2.setInfo(new ClassX());
// also.. how? I'm setting a class that's out of the allowed range +
// seemingly violating the PECS for <? extends E>
}
如您所见,在组合它们时我完全感到困惑,让我感到非常惊讶的是这些声明可以顺利通过编译器。
我在某处读到,在 Java 中不可能将两个边界组合起来,但那是如何工作的呢?
public class Test {
public interface F {}
public static class E implements F {}
public static class D extends E {}
public static class C extends E {}
public static class B extends C {}
public static class A extends B implements F {}
public static class X extends A {}
public static class Node<T extends E> {
private T info;
public T getInfo() {return info;}
public void setInfo(T info) {this.info = info;}
}
public static void main(String[] args) {
Node<? super B> n = new Node<C>(); // C is superclass of B = OK
F i = n.getInfo(); // node type = B|C|E all these types implements F (since E implements F) = OK
n.setInfo(new X()); // X has supertypes A,B,C,E = can be casted to B and so satisfy <? super B>
}
}
免责声明:我不知道具体的类型推断规则,但请按照我的最佳理解进行解释。
关于 InterfaceF i2 = n2.getInfo()
- 因为 Node<T extends E>
可以肯定 Node.getInfo()
returns 某些东西 extends E
。根据您的描述E implements F
。因此可以确保 T extends E
也将 implement F
。因此Node.getInfo() = T extends E implements F
。所以 n2.getInfo() implements F
没问题。
关于 n2.setInfo(new ClassX())
- 我没有像上面那样正式的解释,但让我们试着考虑一下:基本上你 Node<? super ClassB>
告诉每个人 期待 最多 ClassB
作为Node
内容的最低值。但是,由于 ClassX
可传递地继承 ClassB
它是完全有效的,因为它将满足 ? super ClassB
.
提出的所有接口保证
希望对您有所帮助!
第一行 InterfaceF i2 = n2.getInfo();
编译,因为下界通配符仍然保留类型变量本身的界限。由于类型变量有一个上限ClassE
,getInfo()
仍然returns一个ClassE
。由于 ClassE
实现了 InterfaceF
,赋值编译。
换句话说,我们可以想象,当您执行 Node<? super ClassB>
时,您实际上隐含地执行了类似(虚构语法)Node<? extends ClassE & super ClassB>
的操作。 Node
的类型参数既是 ClassB
的超类型 又是 的子类型 ClassE
.
这类似于 Node<?>
与 Node<? extends ClassE>
隐式相同的方式。
实际指定的方式有点复杂,但它在 capture conversion 中。捕获转换是编译器采用带通配符的类型并将其视为不带通配符的类型的过程,以便确定子类型。
Let G
name a generic type declaration with n type parameters A<sub>1</sub>,...,A<sub>n</sub>
with corresponding bounds U<sub>1</sub>,...,U<sub>n</sub>
.
There exists a capture conversion from a parameterized type G<T<sub>1</sub>,...,T<sub>n</sub>>
to a parameterized type G<S<sub>1</sub>,...,S<sub>n</sub>
, where, for 1 ≤ i ≤ n :
[...]
If T<sub>i</sub>
is a wildcard type argument of the form ? super B<sub>i</sub>
, then S<sub>i</sub>
is a fresh type variable whose upper bound is U<sub>i</sub>[A<sub>1</sub>:=S<sub>1</sub>,...,A<sub>n</sub>:=S<sub>n</sub>]
and whose lower bound is B<sub>i</sub>
.
也就是说,S<sub>i</sub>
(? super ClassB
对应的捕获转换后的类型参数)获得了下界来自通配符的边界及其上界 来自类型变量声明的边界。
第二行 n2.setInfo(new ClassX());
编译因为 ClassX
是 ClassB
的子类,所以它可以隐式转换为它。我们可以想象 n2
是一个 Node<ClassB>
并且这行编译的原因可能更明显:
Node<ClassB> n2 = ...;
n2.setInfo(new ClassX());
setInfo
接受 ClassB
以及 ClassB
.
的任何子类型
另外,关于这个:
I read somewhere that a combination of both bounds isn't possible in Java, but how does that work then?
编译器中的类型系统做了很多我们自己无法明确做的事情。另一个很好的例子(虽然不相关)是匿名类型推断 类:
int num = Objects.requireNonNull(new Object() {int num = 42;}).num;
System.out.println(num); // 42
它编译是因为允许类型推断推断 requireNonNull
的 T
的类型参数是匿名对象类型,即使我们自己永远无法将该类型作为显式类型参数提供.
我怎么理解的?延伸..和? super .. 自己工作,哪些泛型类型是可能的,但我只是不明白这个层次结构是如何可能的:
-> 表示扩展
类分别是X(最低),A到E(最高)
接口为F
X -> A(实现 F)-> B -> C -> E(实现 F)
还有 D -> E
public class Node<T extends ClassE> {
private T info;
public T getInfo() {
return info;
}
public void setInfo(T info) {
this.info = info;
}
}
public static void main (String [] args){
Node<? super ClassB> n2 = new Node<ClassC>();
// this makes sense, since Node accepts below E and above B
InterfaceF i2 = n2.getInfo();
// how? Not only outside of <? extends E> but also getting value even though
// <? super B> is defined above, what's up with PECS?
n2.setInfo(new ClassX());
// also.. how? I'm setting a class that's out of the allowed range +
// seemingly violating the PECS for <? extends E>
}
如您所见,在组合它们时我完全感到困惑,让我感到非常惊讶的是这些声明可以顺利通过编译器。 我在某处读到,在 Java 中不可能将两个边界组合起来,但那是如何工作的呢?
public class Test {
public interface F {}
public static class E implements F {}
public static class D extends E {}
public static class C extends E {}
public static class B extends C {}
public static class A extends B implements F {}
public static class X extends A {}
public static class Node<T extends E> {
private T info;
public T getInfo() {return info;}
public void setInfo(T info) {this.info = info;}
}
public static void main(String[] args) {
Node<? super B> n = new Node<C>(); // C is superclass of B = OK
F i = n.getInfo(); // node type = B|C|E all these types implements F (since E implements F) = OK
n.setInfo(new X()); // X has supertypes A,B,C,E = can be casted to B and so satisfy <? super B>
}
}
免责声明:我不知道具体的类型推断规则,但请按照我的最佳理解进行解释。
关于 InterfaceF i2 = n2.getInfo()
- 因为 Node<T extends E>
可以肯定 Node.getInfo()
returns 某些东西 extends E
。根据您的描述E implements F
。因此可以确保 T extends E
也将 implement F
。因此Node.getInfo() = T extends E implements F
。所以 n2.getInfo() implements F
没问题。
关于 n2.setInfo(new ClassX())
- 我没有像上面那样正式的解释,但让我们试着考虑一下:基本上你 Node<? super ClassB>
告诉每个人 期待 最多 ClassB
作为Node
内容的最低值。但是,由于 ClassX
可传递地继承 ClassB
它是完全有效的,因为它将满足 ? super ClassB
.
希望对您有所帮助!
第一行 InterfaceF i2 = n2.getInfo();
编译,因为下界通配符仍然保留类型变量本身的界限。由于类型变量有一个上限ClassE
,getInfo()
仍然returns一个ClassE
。由于 ClassE
实现了 InterfaceF
,赋值编译。
换句话说,我们可以想象,当您执行 Node<? super ClassB>
时,您实际上隐含地执行了类似(虚构语法)Node<? extends ClassE & super ClassB>
的操作。 Node
的类型参数既是 ClassB
的超类型 又是 的子类型 ClassE
.
这类似于 Node<?>
与 Node<? extends ClassE>
隐式相同的方式。
实际指定的方式有点复杂,但它在 capture conversion 中。捕获转换是编译器采用带通配符的类型并将其视为不带通配符的类型的过程,以便确定子类型。
Let
G
name a generic type declaration with n type parametersA<sub>1</sub>,...,A<sub>n</sub>
with corresponding boundsU<sub>1</sub>,...,U<sub>n</sub>
.There exists a capture conversion from a parameterized type
G<T<sub>1</sub>,...,T<sub>n</sub>>
to a parameterized typeG<S<sub>1</sub>,...,S<sub>n</sub>
, where, for 1 ≤ i ≤ n :
[...]
If
T<sub>i</sub>
is a wildcard type argument of the form? super B<sub>i</sub>
, thenS<sub>i</sub>
is a fresh type variable whose upper bound isU<sub>i</sub>[A<sub>1</sub>:=S<sub>1</sub>,...,A<sub>n</sub>:=S<sub>n</sub>]
and whose lower bound isB<sub>i</sub>
.
也就是说,S<sub>i</sub>
(? super ClassB
对应的捕获转换后的类型参数)获得了下界来自通配符的边界及其上界 来自类型变量声明的边界。
第二行 n2.setInfo(new ClassX());
编译因为 ClassX
是 ClassB
的子类,所以它可以隐式转换为它。我们可以想象 n2
是一个 Node<ClassB>
并且这行编译的原因可能更明显:
Node<ClassB> n2 = ...;
n2.setInfo(new ClassX());
setInfo
接受 ClassB
以及 ClassB
.
另外,关于这个:
I read somewhere that a combination of both bounds isn't possible in Java, but how does that work then?
编译器中的类型系统做了很多我们自己无法明确做的事情。另一个很好的例子(虽然不相关)是匿名类型推断 类:
int num = Objects.requireNonNull(new Object() {int num = 42;}).num;
System.out.println(num); // 42
它编译是因为允许类型推断推断 requireNonNull
的 T
的类型参数是匿名对象类型,即使我们自己永远无法将该类型作为显式类型参数提供.