首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >用Java重写泛型方法的适当方法

用Java重写泛型方法的适当方法
EN

Stack Overflow用户
提问于 2019-07-15 17:25:55
回答 3查看 107关注 0票数 2

考虑以下代码:

代码语言:javascript
复制
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

问题

  • 我理解为什么这句话:List<SomeDummyClass> someDummyClasses = a.foo();没有给出任何编译时错误。但在运行时,我认为它应该提供一个ClassCastException。这里到底发生了什么?
  • 当我做System.out.println(someDummyClasses.get(0));时,我得到了ClassCastException。为何会这样呢?
  • 是否有更好的方法来重写这些代码,使代码变得不那么脆弱?
EN

回答 3

Stack Overflow用户

回答已采纳

发布于 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

代码语言:javascript
复制
A a = new B();  

所以在这里:

代码语言:javascript
复制
List<SomeDummyClass> someDummyClasses = a.foo();

在编译时,foo()引用接口中声明的<E> List<E> foo()方法,并且设计了一个与E不受限制的泛型的方法(因此是Object),以使声明的返回类型适应目标:这里是方法的客户机为分配方法返回而声明的变量的类型。

将声明更改为:

代码语言:javascript
复制
B b = new B();  
List<SomeDummyClass> someDummyClasses = b.foo();

而汇编甚至都不会通过。

请注意,您的问题不特定于重写方法。声明一个方法(该方法还会产生关于未检查的强制转换的警告):

代码语言:javascript
复制
public static <T> List<T> getListOfAny(){
    return (List<T>) new ArrayList<Integer>();
}

你可以用同样不一致的方式使用它:

代码语言:javascript
复制
 List<String> listOfAny = getListOfAny();

这个故事的寓意是:不考虑编译器产生的未经检查的转换警告,比如化妆品/细节,而是正确处理它们,您可以从泛型.带来的编译检查中获得尽可能多的好处。

票数 2
EN

Stack Overflow用户

发布于 2019-07-15 18:07:06

但在运行时,我认为它应该提供一个ClassCastException。这里到底发生了什么?

请记住,Java中的泛型纯粹是编译时的事情。编译代码时,编译器将删除所有泛型参数,并在必要时添加强制转换。运行时的代码如下所示:

代码语言:javascript
复制
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

代码语言:javascript
复制
SomeDummyClass obj = someDummyClasses.get(0);

因为上面的代码将在运行时变成这样:

代码语言:javascript
复制
SomeDummyClass obj = (SomeDummyClass)someDummyClasses.get(0);

当我做System.out.println(someDummyClasses.get(0))时,我得到一个ClassCastException。为何会这样呢?

不,不知道

是否有更好的方法来重写这些代码,使代码变得不那么脆弱?

(请注意,我对紫提花知之甚少)

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

代码语言:javascript
复制
interface A<T> {
    List<T> foo();
}

然后做B implements A<Object>。这样,至少更安全。

票数 1
EN

Stack Overflow用户

发布于 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超类的类型

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/57044548

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档