当前位置: 首页 > 面试题库 >

通用方法实现中的不同返回值类型

杜辰龙
2023-03-14
问题内容

今天,我偶然发现了一些我什至没想到可以编译的Java代码。减少到最低限度,它看起来像这样:

import java.util.List;

interface A {
    <T> List<String> foo();
}

interface B {
    <T> List<Integer> foo();
}

class C implements A, B {
    @Override
    public List<?> foo()
    {
        return null;
    }
}

乍一看,类型参数<T>foo的方法AB期待,因为没有必要T,不使用其他任何地方。无论如何,我发现这在允许冲突的返回值类型共存于同一实现中起着至关重要的作用:如果<T>忽略了一个或两个,则代码不会编译。这里是非工作版本:

import java.util.List;

interface A {
    List<String> foo();
}

interface B {
    List<Integer> foo();
}

class C implements A, B {
    @Override
    public List<?> foo()
    {
        return null;
    }
}

我不需要修复上面的代码片段,因为这些只是我用来解释我观点的示例。我只是想知道为什么编译器对它们的行为有所不同。有人可以解释到底有什么规则在起作用吗?


问题答案:

虽然第一个示例确实进行了编译,但将给出未经检查的转换警告:

// Type safety: The return type List<?> for foo() from the type C needs
// unchecked conversion to conform to List<String>
public List<?> foo()
{
    return null;
}

这里发生的事情是,通过声明类型的参数,A.foo()并且B.foo()是通用的方法。然后,覆盖C.foo()忽略该类型参数。这类似于使用原始类型,本质上是“退出”该方法签名的通用类型检查。这会导致使用该继承的方法的编译器
擦除

代替:List<String> foo()List<Integer> foo()二者成为List foo(),其因此可以通过被实现C.foo()

您可以看到,通过将类型参数保留在C.foo()声明中,将会出现预期的编译器错误:

// The return type is incompatible with A.foo()
public <T> List<?> foo()
{
    return null;
}

同样,如果两个接口方法中的任何一个都不声明类型参数,则从覆盖中省略类型参数将无法“选择退出”该方法的通用类型检查,并且返回类型List<?>仍然不兼容。

JLS§8.4.2涵盖了此行为:

子签名的概念旨在表示两种方法之间的关系,这些方法的签名不完全相同,但是其中一个可以覆盖另一个方法。具体来说,它允许签名不使用泛型类型的方法覆盖该方法的任何泛化版本。这很重要,因此库设计者可以独立于定义库的子类或子接口的客户端自由地泛化方法。

Angelika
Langer的泛型常见问题解答在其部分中对此行为进行了扩展。非泛型方法可以覆盖泛型方法吗?:

现在,让我们探讨一个非泛型子类型方法覆盖泛型超类型方法的示例。如果签名的擦除相同,则将非通用子类型方法视为通用超类型方法的替代版本。

示例(非泛型子类型方法覆盖泛型超类型方法):

class Super {
  public <T> void set( T arg) { ... }
  public <T> T get() { ... }
}
class Sub extends Super {
  public void set( Object arg) { ... } // overrides
  public Object get() { ... }    // overrides with unchecked warning
}
warning: get() in Sub overrides <T>get() in Super;  
return type requires unchecked conversion
found   : Object
required: T
        public Object get() {

在此,子类型方法具有签名,即set(Object)get() ,与超级类型方法的擦除相同。这些类型擦除的签名被视为等效。

get方法有一个缺陷:因为返回类型不是真正兼容的,所以我们收到未经检查的警告。亚型方法的返回类型getObject,该超GET方法的返回类型是一个无限制类型参数。子类型方法的返回类型既不与超类型方法的返回类型相同,也不是其子类型。在这两种情况下,编译器都会欣然接受兼容的返回类型。而是子类型方法的返回类型Object可以通过未经检查的转换转换为超类型方法的返回类型。未经检查的警告表明,必须执行类型检查,编译器和虚拟机均不能执行该检查。换句话说,未检查的操作不是类型安全的。如果是可转换的返回类型,则必须确保子类型方法的返回值与超类型方法的返回类型在类型上兼容,但是除程序员之外,没有人可以确保这一点。



 类似资料:
  • 问题内容: 这就是我想要做的。使用反射,我想获取所有方法及其返回类型(非泛型)。我一直在这样做。但是,当我遇到返回类型未知的方法时,我遇到了限制。 我如何知道方法的返回类型是否通用?我没有奢求在运行时拥有对象。我正在尝试使用Google 或使用抽象类来获取类型信息。我想关联到对象的方法。 有人认为Java不保留通用信息。在这种情况下,为什么第一次强制转换有效而第二次强制转换无效。 任何帮助表示赞赏

  • 问题内容: 考虑以下代码: 这会导致以下错误: 类型B和A不兼容;都定义了another(),但是返回类型不相关 我已经看到了这样的问题,并按照公认的答案中的不兼容示例进行操作-即 但是,在那种情况下,返回类型确实是不兼容的-返回类型不能同时为void和布尔值。而在上面的示例中,返回类型是an 和a ,因此可以实现两个扩展接口。 此外,在查看了JLS(8.4.8、8.4.8.3、8.4.8.4)之

  • 我正在尝试实现和重写具有不同返回类型的方法,而不会被迫转换返回类型。 我的问题:是否可以在不强制强制转换的情况下返回不同的类型?解决这个问题的抽象方法看起来怎么样? 我认为必须有一个解决方案,因为编译器应该知道返回类型...

  • 在我的应用程序中,有一个计算得到两个日期之间的天数。计算如下所示; 此方法在我的本地pc(windows 7,64位)上返回正确的值(即2015-03-10 00:00:00.0、2015-03-02 00:00:00.0,从存储库中检索的值为8,并传递给该方法)。但是,此方法在位于加拿大的pc(Windows 7,64位)上返回不正确的值(即2015-03-10 00:00:00.0、2015-

  • 该方法应返回类型Animal的值。如果出现问题,我想指出存在错误,正确的技术是什么?以防我不想出错。我可以返回什么来代替动物类型?

  • 问题内容: 。 将返回String和int值。因此,我从这些返回值中得出了以下解决方案。 建立课程通话 并进行如下更改。 现在我的问题是,有没有更简单的方法来实现这一目标? 问题答案: 最后,我认为我的方法会更好,因为当返回类型的数量增加时,这种实现会以最佳方式实现。