Java 泛型中的 Get-put 原则
Get-put principles in Java generics
我最近在学习 Java 泛型并遇到了所谓的 "get-put" 原则,即哪种通配符允许您从集合中添加或删除某些类型的对象(参考,例如 https://flylib.com/books/en/4.79.1.18/1/).
我的问题是,据说您只能从使用 <? super SomeClass>
的集合中获取对象以外的任何东西。但是下面的代码是完全有效的:
List<? super A> list = new ArrayList<>();
list.add(new A());
System.out.println((list.get(0).toString()));
哪里
class A{
@Override
public String toString(){
return "super.toString();";
}
}
搞笑的是,居然用了重写的toString(),违背了原理。
此外,
A a = list.get(0);
失败。
谁能解释一下这是什么问题?
本例中 list.get()
的引用类型是 Object
,而 class 声明了 toString()
所以你可以调用它。
但是Java使用dynamic dispatch来选择执行哪个方法。由于对象的运行时类型是 A
,这就是调用的版本。
您的代码等同于:
Object obj = new A();
System.out.println(obj.toString()); /* Prints A's version */
A a = obj; /* Fails to compile. */
如您所见,该行为与泛型无关。
这里没有发生意外。让我们 运行 完成以下步骤:
List<? super A> list = new ArrayList<>();
我们有 List
个 ?
,在 class 层次结构中位于 A
之上。每个?
都是一个Object
,所以我们可以把它看成一个List<Object>
。继续前进...
list.add(new A());
System.out.println((list.get(0)
到目前为止一切正常 - list
内部有一个 A
,它被提取为 Object
。
.toString()));
我们调用 toString
,它是在 Object
(A
)上调用的。动态分派继续调用 A
的 toString
方法(类型层次结构中的最低定义)。然而,这是完全合法的,因为 toString
是为 Object
和 A
定义的。继续...
A a = list.get(0); //oops!
正如预期的那样,这中断了,因为我们尝试将 Object
从 List<Object>
转换为 A
,而不进行转换(例如 A a = (A) list.get(0);
)。
我最近在学习 Java 泛型并遇到了所谓的 "get-put" 原则,即哪种通配符允许您从集合中添加或删除某些类型的对象(参考,例如 https://flylib.com/books/en/4.79.1.18/1/).
我的问题是,据说您只能从使用 <? super SomeClass>
的集合中获取对象以外的任何东西。但是下面的代码是完全有效的:
List<? super A> list = new ArrayList<>();
list.add(new A());
System.out.println((list.get(0).toString()));
哪里
class A{
@Override
public String toString(){
return "super.toString();";
}
}
搞笑的是,居然用了重写的toString(),违背了原理。
此外,
A a = list.get(0);
失败。
谁能解释一下这是什么问题?
本例中 list.get()
的引用类型是 Object
,而 class 声明了 toString()
所以你可以调用它。
但是Java使用dynamic dispatch来选择执行哪个方法。由于对象的运行时类型是 A
,这就是调用的版本。
您的代码等同于:
Object obj = new A();
System.out.println(obj.toString()); /* Prints A's version */
A a = obj; /* Fails to compile. */
如您所见,该行为与泛型无关。
这里没有发生意外。让我们 运行 完成以下步骤:
List<? super A> list = new ArrayList<>();
我们有 List
个 ?
,在 class 层次结构中位于 A
之上。每个?
都是一个Object
,所以我们可以把它看成一个List<Object>
。继续前进...
list.add(new A());
System.out.println((list.get(0)
到目前为止一切正常 - list
内部有一个 A
,它被提取为 Object
。
.toString()));
我们调用 toString
,它是在 Object
(A
)上调用的。动态分派继续调用 A
的 toString
方法(类型层次结构中的最低定义)。然而,这是完全合法的,因为 toString
是为 Object
和 A
定义的。继续...
A a = list.get(0); //oops!
正如预期的那样,这中断了,因为我们尝试将 Object
从 List<Object>
转换为 A
,而不进行转换(例如 A a = (A) list.get(0);
)。