Java中重写泛型方法的正确方法

Java中重写泛型方法的正确方法,java,generics,type-erasure,raw-types,Java,Generics,Type Erasure,Raw Types,考虑以下代码: 导入java.util.array; 导入java.util.List; 类人{} 类OtherPerson{} 类SomeDummyClass{} 接口A{ List foo(); } 类B实现了一个{ @凌驾 公开名单foo(){ 返回Arrays.asList(Integer.valueOf(3)、newperson()、newotherperson()); } } 公共班机{ 公共静态void main(字符串[]args){ A=新的B(); 列出someDummyCl

考虑以下代码:

导入java.util.array;
导入java.util.List;
类人{}
类OtherPerson{}
类SomeDummyClass{}
接口A{
List foo();
}
类B实现了一个{
@凌驾
公开名单foo(){
返回Arrays.asList(Integer.valueOf(3)、newperson()、newotherperson());
}
}
公共班机{
公共静态void main(字符串[]args){
A=新的B();
列出someDummyClasses=a.foo();
System.out.println(someDummyClasses.get(0));//打印3
}
}
我不明白为什么我们可以重写
foo()
方法而不产生任何编译时错误。当我编写
List someDummyClasses=a.foo()时,这一点就变得不那么直观了这是完全有效的(没有编译时错误)

这种情况的一个实时示例是使用
mybatis-3

  • A
    模仿
  • B
    mimics(这个类不是包私有的-我想它应该是这样的)
  • foo()
    模仿
问题
  • 我明白为什么这句话:
    List someDummyClasses=a.foo()未给出任何编译时错误。但在运行时,我认为它应该给出一个
    ClassCastException
    。这里到底发生了什么
  • 当我执行
    System.out.println(someDummyClasses.get(0))我得到一个
    ClassCastException
    。为什么会这样
  • 有没有更好的方法来重新编写这些代码,使代码变得不那么脆弱
1)A#foo的签名是返回一个
列表
,该列表将返回一个在关联时被捕获为E的类型列表,
列表=新的A().foo()
(如果A是一个具体的类)编译,因为它捕获了该类型,而
列表=新的B().foo()
返回一个
列表
,并且无法分配

第二)由于给列表分配了整数和字符串,因此得到了ClassCastException

3)您的界面可能有类似的
listfoo()将B类上的声明转换为无效等,由于您事先知道
E

但在运行时,我认为它应该给出一个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
s了

如果您这样做,将出现
ClassCastException

SomeDummyClass obj = someDummyClasses.get(0);
因为上面的代码在运行时会变成这样:

SomeDummyClass obj = (SomeDummyClass)someDummyClasses.get(0);
当我执行System.out.println(someDummyClasses.get(0))时;我得到一个
ClassCastException
。为什么会这样

有没有更好的方法来重新编写这些代码,使代码变得不那么脆弱

(请注意,我对mybatis了解不多)

我认为您应该在这里做的是使
成为一个
通用接口:

interface A<T> {
    List<T> foo();
}
接口A{
List foo();
}
并使
B实现A
。这样,至少它更安全

我不明白为什么我们可以用任何编译重写foo()方法 时间错了

是,但您有一个编译警告,因为重写方法
List
中的返回类型比接口方法中的
List
更具体:

未经检查的覆盖:返回类型需要未经检查的转换。建立
java.util.List
,必需
java.util.List

不处理此类警告可能会导致泛型的使用不一致。这里有一个很好的例子

关于:

当我写这篇文章时,这一点就变得不那么直观了 列出someDummyClasses=a.foo();这是完美的 有效(无编译时错误)

为什么会出现编译错误

这里您将
a
声明为
a

A a = new B();  
因此,这里:

List<SomeDummyClass> someDummyClasses = a.foo();
而这一汇编甚至不会通过

请注意,您的问题并不特定于重写方法。 声明一个方法(该方法还生成有关未选中强制转换的警告):

公共静态列表getListOfAny(){
返回(列表)新的ArrayList();
}
你可以用同样的方式使用它:

 List<String> listOfAny = getListOfAny();
List listofay=getlistofay();

<> P>这个故事的寓意:<强>不要考虑编译器所产生的未经检查的转换警告,比如化妆品/细节,但要正确处理它们,你可以从泛型带来的编译检查中尽可能多地受益。

我假设Heg感谢答案。我确实无意中忽略了编译器的警告。谢谢你指出这一点。不客气。对于这个故事来说,几天前不可能理解编译器的奇怪行为。事实上,这是由于当前怪异代码的未经检查的转换造成的。最后,我将把IDE中未锁定转换警告的严重性级别提高到error:)+1,以了解编译后的效果。链接不起作用。你能更新一下吗?我同意
A
更有意义,如果我在写库的话,我会选择这个选项。我想知道为什么mybatis没有这样做(可能是因为它最终会在许多类型上参数化接口)@LavishKothari这很奇怪。这个链接仍在为我工作。试试这个:
public static <T> List<T> getListOfAny(){
    return (List<T>) new ArrayList<Integer>();
}
 List<String> listOfAny = getListOfAny();