【问题标题】:jls. return-type-substitutable. What does it mean?jls。返回类型可替换。这是什么意思?
【发布时间】:2014-05-11 14:42:41
【问题描述】:

我正在阅读 jls 并且遇到以下术语:

return-type-substitutable

来自 jls 的 sn-p

返回类型为 R1 的方法声明 d1 是 返回类型可替代具有返回类型 R2 的另一种方法 d2 如果以下任何一项为真:

如果 R1 无效,则 R2 无效。

如果 R1 是原始类型,则 R2 与 R1 相同。

如果 R1 是引用类型,则以下情况之一为真:

--R1,适应于d2的类型参数(§8.4.4),是一个子类型 R2。

--R1 可以通过未经检查的转换转换为 R2 的子类型 (§5.1.9)。

--d1 与 d2 (§8.4.2) 没有相同的签名,并且 R1 = |R2|。

前两点很清楚。

你能说清楚吗

--R1,适配d2的类型参数(§8.4.4),是R2的子类型。

--R1 可以通过未经检查的转换(第 5.1.9 节)转换为 R2 的子类型。

--d1 与 d2 (§8.4.2) 没有相同的签名,并且 R1 = |R2|。

谢谢

附: 路易吉·门多萨

interface Foo {
        List<String> foo(String arg1, String arg2);
}

class Bar implements Foo {
    @Override
    public ArrayList<String> foo(String arg1, String arg2) {
        //implementation...
        return  null;
    }

    public String foo(String arg1, String arg2, String arg3) {
        //implementation...
        return  null;
    }
}

它是工作变体。

我的问题的原因 - 我想了解 jls 中的以下短语:

如果返回类型为 R1 的方法声明 d1 覆盖或隐藏 用返回类型 R2 声明另一个方法 d2,那么 d1 必须是 d2 的返回类型可替换(第 8.4.5 节)或编译时错误 发生

规则:

If R1 is a reference type then **one of the following** is true:
...
--d1 does not have the same signature as d2 (§8.4.2), and R1 = |R2|.
...

代码:

interface Foo {
        List<String> foo(String arg1, String arg2);
}

class Bar implements Foo {
    @Override
    public List<String> anotherName(String arg1, String arg2,Object obj) {
           return  null;
    }

这是编译错误。

R1==R2 (List&lt;String &gt; == List&lt;String&gt;)

d1!=d2

我在哪里违反了规则?

【问题讨论】:

  • 从您上次的编辑开始,没有 anotherName 方法可以覆盖,因此您会收到编译器错误。此外,您说Bar 实现了Foo,但Bar 类中没有public List&lt;String&gt; foo(String arg1, String arg2) 方法(或另一个签名foo(String arg1, String arg2) 的方法返回ListList 的子类型)。跨度>
  • @Luiggi Mendoza 我写了规则,并根据我对这条规则的看法编写了代码示例。你能指定我违反规则的地方吗?
  • 我已经在我的评论中指出了它们。
  • @Luiggi Mendoza 您的评论很常见。我接受了。但这是你的java可以理解的。想象一下,您是 Java 新手并试图遵守规则。请使用以下格式添加到您的问题更新中:引用规则 - 我的代码中的违规。
  • 我说的是 2 条规则:1。 如果具有返回类型 R1 的方法声明 d1 覆盖或隐藏具有返回类型 R2 的另一个方法 d2 的声明,则 d1 必须是 d2 的返回类型可替换(第 8.4.5 节),否则会出现编译时错误发生为 d2(第 8.4.2 节),并且 R1 = |R2|

标签: java inheritance methods return jls


【解决方案1】:

让我们拥有这个界面

interface Foo {
    List<String> foo(String arg1, String arg2);
}

还有一个实现它的类

class Bar implements Foo {
    @Override
    public List<String> foo(String arg1, String arg2) {
        //implementation...
    }
}

我们有:

  • Bar#food1
  • List&lt;String&gt; 在 d1 中返回类型为 R1。
  • Foo#food2
  • List&lt;String&gt; 在 d2 中返回类型为 R2。

R1,适配d2的类型参数(§8.4.4),是R2的子类型。

这意味着R1可以是R2的子类型,这意味着R1应该通过IS-A测试。所以,我们可以做到以下几点:

class Bar implements Foo {
    @Override
    public ArrayList<String> foo(String arg1, String arg2) {
        //implementation...
    }
}

可以通过未经检查的转换 (§5.1.9) 将 R1 转换为 R2 的子类型。

这与泛型有关。这意味着R1 应该通过 IS-A 测试,即使它针对未经检查的覆盖抛出警告。所以,我们可以做到以下几点:

class Bar implements Foo {
    @Override
    public ArrayList foo(String arg1, String arg2) {
        //implementation...
    }
}

d1 与 d2 (§8.4.2) 没有相同的签名,并且 R1 = |R2|

这意味着重载:

class Bar implements Foo {
    @Override
    public ArrayList<String> foo(String arg1, String arg2) {
        //implementation...
    }

    public ArrayList<String> foo(String arg1, String arg2, String arg3) {
        //implementation...
    }
}

【讨论】:

  • d1 没有与 d2 相同的签名 对于 jls 来说不够精确 - 我绝对可以写任何签名。它是什么意思** R1 = |R2| **
  • @gstackoverflow 这意味着您可以为不同的签名返回相同的类型,这是重载的另一种说法。
  • 重载 - 方法名称相同但参数列表不同的情况。它与返回类型无关
  • tge R2 周围的大括号怎么样?
  • @gstackoverflow 它指的是类类型。
【解决方案2】:

如果返回类型为 R1 的方法声明 d1 覆盖或隐藏了返回类型为 R2 的另一个方法 d2 的声明,则 d1 必须是 d2 的返回类型可替换(第 8.4.5 节),否则会出现编译时错误以 d2 (§8.4.2) 的形式出现,并且 R1 = |R2|。

让我们分解一下:

首先,我们需要一个方法声明d2和一个返回类型R2

class SomeClass {
    public List<String> d2() {
        return null;
    }
}

现在,我们定义一个扩展 SomeClass 的类,它定义了 d1 并返回 R1

class AnotherClass extends SomeClass {
    //@Override annotation means it is overriding a method in parent class
    //d2 here is d1
    //Object here is R1
    @Override
    public List<String> d2() {
    }
}

如何编译?因为d1 可以替换d2,这意味着R1 可以替换R2 作为返回类型。这也称为covariant return type,并在Java Tutorials. Returning a Value from a Method 中进行了介绍。另一条规则支持这一点:

如果以下任何一项为真,则返回类型为 R1 的方法声明 d1 可替代返回类型为 R2 的另一个方法 d2:如果 R1 是引用类型,则以下条件之一为真:d1 不与 d2 (§8.4.2) 具有相同的签名,并且 R1 = |R2|。

这意味着所有这些方法都可以有效地覆盖SomeClass#d2

class AnotherClass extends SomeClass {
    //ArrayList implements List
    @Override
    public ArrayList<String> d2() {
        return null;
    }
}

class AnotherClass extends SomeClass {
    //raw List can be casted to `List<String>` by unchecked conversion
    //means you don't need a explicit cast to convert `List` to `List<String>`
    //due to type erasure
    @Override
    public List d2() {
        return null;
    }
}

class AnotherClass extends SomeClass {
    //combination of both examples above
    @Override
    public ArrayList d2() {
        return null;
    }
}

但这些都是无效的实现:

class AnotherClass extends SomeClass {
    //the signature is not the same
    @Override
    public List<String> d1() {
        return null;
    }
}

class AnotherClass extends SomeClass {
    //Set is not a subtype of List
    @Override
    public Set<String> d2() {
        return null;
    }
}

class AnotherClass extends SomeClass {
    //Collection is not a subtype of List
    @Override
    public Collection<String> d2() {
        return null;
    }
}

【讨论】:

  • 以下短语的问题:d1 与 d2 没有相同的签名
  • @gstackoverflow 你知道什么是签名吗?
  • 是方法名和参数列表
  • @gstackoverflow 好的,那么在最后一段代码中,d1d2 没有相同的签名,因此编译器错误。哪一部分你不明白?
  • 根据这些规则 ** 如果 d1 没有与 d2 相同的签名(第 8.4.2 节),并且 R1 = |R2|** 那么 返回类型 R1 的方法声明 d1 是返回类型可替代另一种方法 d2 然后 ...
猜你喜欢
  • 1970-01-01
  • 2020-07-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-10-03
  • 2017-12-03
  • 1970-01-01
相关资源
最近更新 更多