考虑以下代码:
import java.util.Arrays;
import java.util.List;
class Person {}
class OtherPerson {}
class SomeDummyClass {}
interface A {
<E>List<E> foo();
}
class B implements A {
@Override
public List<Object> foo() {
return Arrays.asList(Integer.valueOf(3), new Person(), new OtherPerson());
}
}
public class Main {
public static void main(String[] args) {
A a = new B();
List<SomeDummyClass> someDummyClasses = a.foo();
System.out.println(someDummyClasses.get(0)); // prints 3
}
}我不明白为什么我们可以在没有任何编译时错误的情况下重写foo()方法。对于我编写List<SomeDummyClass> someDummyClasses = a.foo();时的推理来说,这尤其不那么直观,而且这是完全有效的(没有编译时错误)。
这种情况的一个实时示例是mybatis-3。
A模仿ResultHandlerB模仿DefaultResultSetHandler (这个类不是包私有的--我以为它应该是)foo()模仿handleResultSets问题
List<SomeDummyClass> someDummyClasses = a.foo();没有给出任何编译时错误。但在运行时,我认为它应该提供一个ClassCastException。这里到底发生了什么?System.out.println(someDummyClasses.get(0));时,我得到了ClassCastException。为何会这样呢?发布于 2019-07-15 20:01:11
我不明白为什么我们可以用任何编译时errros覆盖foo()方法。
是的,但是您有一个编译警告,因为覆盖方法List<Object>中的返回类型比接口方法中的List<E>更具体:
未选中重写:返回类型需要未经检查的转换。找到了
java.util.List<java.lang.Object>,必需的java.util.List<E>。
如果不处理这类警告,可能会导致泛型的使用不一致。这里有一个很好的例子。
关于:
当我编写List someDummyClasses =a.foo()时,这一点尤其不直观;这是完全有效的(没有编译时错误)。
为什么你会得到一个编译错误?
这里您将a声明为A:
A a = new B(); 所以在这里:
List<SomeDummyClass> someDummyClasses = a.foo();在编译时,foo()引用接口中声明的<E> List<E> foo()方法,并且设计了一个与E不受限制的泛型的方法(因此是Object),以使声明的返回类型适应目标:这里是方法的客户机为分配方法返回而声明的变量的类型。
将声明更改为:
B b = new B();
List<SomeDummyClass> someDummyClasses = b.foo();而汇编甚至都不会通过。
请注意,您的问题不特定于重写方法。声明一个方法(该方法还会产生关于未检查的强制转换的警告):
public static <T> List<T> getListOfAny(){
return (List<T>) new ArrayList<Integer>();
}你可以用同样不一致的方式使用它:
List<String> listOfAny = getListOfAny();这个故事的寓意是:不考虑编译器产生的未经检查的转换警告,比如化妆品/细节,而是正确处理它们,您可以从泛型.带来的编译检查中获得尽可能多的好处。
发布于 2019-07-15 18:07:06
但在运行时,我认为它应该提供一个ClassCastException。这里到底发生了什么?
请记住,Java中的泛型纯粹是编译时的事情。编译代码时,编译器将删除所有泛型参数,并在必要时添加强制转换。运行时的代码如下所示:
interface A {
List foo();
}
class B implements A {
@Override
public List foo() {
return Arrays.asList(Integer.valueOf(3), new Person(), new OtherPerson());
}
}
// ...
List someDummyClasses = a.foo();
System.out.println(someDummyClasses.get(0));看!这里没什么不对的。没有强制转换,更不用说ClassCastException了。
如果您这样做,就会有一个ClassCastException:
SomeDummyClass obj = someDummyClasses.get(0);因为上面的代码将在运行时变成这样:
SomeDummyClass obj = (SomeDummyClass)someDummyClasses.get(0);当我做System.out.println(someDummyClasses.get(0))时,我得到一个
ClassCastException。为何会这样呢?
是否有更好的方法来重写这些代码,使代码变得不那么脆弱?
(请注意,我对紫提花知之甚少)
我认为您在这里应该做的是使A成为一个通用接口:
interface A<T> {
List<T> foo();
}然后做B implements A<Object>。这样,至少更安全。
发布于 2019-07-15 17:42:20
1) A#foo的签名是返回一个<E> List<E>,它将返回一个类型列表,该列表将作为E在分配时捕获,List<X> = new A().foo() (如果A是一个具体的类)编译,因为它捕获了类型,而List<X> = new B().foo()返回一个List<Object>,不能分配。
2)您获得了一个ClassCastException,因为您将整数和字符串分配给了一个列表
3)您的接口可以像<E extends SummyDummyClass> List<E> foo();一样,将B类上的声明转换为无效等等,而且您还需要更少的强制转换,因为您事先知道E超类的类型
https://stackoverflow.com/questions/57044548
复制相似问题