Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/321.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
不允许在Java方法重写上使用超类型的原因是什么?_Java_Overloading - Fatal编程技术网

不允许在Java方法重写上使用超类型的原因是什么?

不允许在Java方法重写上使用超类型的原因是什么?,java,overloading,Java,Overloading,编译器认为以下代码无效: class Foo { void foo(String foo) { ... } } class Bar extends Foo { @Override void foo(Object foo) { ... } } 我认为这在“m1的签名是m2签名的子签名(§8.4.2)”和8.4.2“相应类型变量的边界相同”中有描述 我的问题是:为什么子类型(Bar)中的参数不能是超类型(Foo)中参数的超类型。在示例中,对象是字符串的超类型。就我所知,允

编译器认为以下代码无效:

class Foo {
    void foo(String foo) { ... }
}

class Bar extends Foo {
    @Override
    void foo(Object foo) { ... }
}
我认为这在“m1的签名是m2签名的子签名(§8.4.2)”和8.4.2“相应类型变量的边界相同”中有描述

我的问题是:为什么子类型(Bar)中的参数不能是超类型(Foo)中参数的超类型。在示例中,对象是字符串的超类型。就我所知,允许这样做不会违反法律


是否存在允许这样做会破坏代码的场景,或者这是当前JLS的限制?

假设您可以这样做。现在,您的超类如下所示:

class Foo {
    void foo(String foo) { ... }
    void foo(Number foo) { ... }
}
class Foo {
    public void foo(String foo) { ... }
}

class Bar extends Foo {
    @Override
    public void foo(String foo) { this.foo((Object)foo); }
    public void foo(Object foo) { ... }
}
现在你的子类是:

class Bar extends Foo {
    @Override
    void foo(Object foo) { ... }
}
该语言可能允许这样的事情(只需将Foo.Foo(字符串)和Foo.Foo(数字)分派给Bar.Foo(对象)),但显然Java的设计决策是一个方法只能覆盖另一个方法

[编辑]

正如dasblinkenlight在他的回答中所说的,一个人可以拥有一个没有@Override的foo(对象),但这只是重载foo函数,而不是重写它们。调用时,java选择最具体的方法,因此foo(“Hello World”)将始终被分派到foo(String)方法。

(从另一个角度重写…我的原始答案包含错误。:()

为什么子类型(Bar)中的参数不能是超类型(Foo)中参数的超类型。

我相信从技术上讲它可以,而且它不会破坏遵循类型替换(Liskov替换原则)的祖先契约

  • 类型按值传递(包括引用类型)
  • 调用方永远不能被迫处理与其传入的参数类型不同的参数类型
  • 方法体可以交换参数类型,但不能将其返回给调用方(没有“输出参数类型”之类的东西)
  • 如果您的建议被允许,并且对祖先方法签名进行了调用,则Decent类可以使用更广泛的类型覆盖该方法,但是仍然不可能返回比调用方设置的更广泛的类型
  • 重写永远不会破坏使用狭义祖先方法契约的客户端
根据我下面的分析,我推测/猜测不允许您的方案的理由:

  • 与性能相关的:允许覆盖更广泛的类型会影响运行时性能,这是一个主要问题
  • 与功能相关的:它只添加了少量功能。目前,您可以添加“更广泛的方法”作为重载方法,而无需重写。然后您还可以使用精确的签名匹配来重写原始方法。最终结果是:您在功能上实现了非常类似的结果

方法重写的编译器要求-JLS 7

编译器需要根据您的经验进行操作。

子类中的方法可以重写祖先类中的方法:

  • 方法名称相同(
  • 删除泛型类型参数后,方法参数具有相同的类型(8.4.28.4.8.1
  • 返回类型是可替代祖先类中返回类型的类型,即相同类型或更窄的类型(

    注意:子签名并不意味着重写方法使用被重写方法的子类型。如果重写方法具有完全相同的类型签名,则称重写方法具有被重写方法的子签名,但泛型类型和相应的原始类型被认为是等效的


用于方法匹配和调用的编译器v运行时处理

通过多态类型匹配有一个性能命中匹配方法签名。通过将覆盖方法签名限制为祖先的精确匹配,JLS将此过程的大部分转移到编译时。-总结:

  • 确定要搜索的类或接口(编译时确定)

    • 获取调用方法的基类型T
    • 这是向编译器声明的引用类型,而不是运行时类型(可以替换子类型)
  • 确定方法签名(编译时确定)

    • 搜索编译时基类型T,查找与名称和参数匹配的适用方法,以及与调用一致的返回类型。
      • 解析T的泛型参数,该参数可以是显式传递的,也可以是从调用方法参数的类型隐式推断的
      • 阶段1:通过一致类型/子类型(“子类型”)适用的方法
      • 第2阶段:通过自动装箱/拆箱和子类型应用的方法
      • 阶段3:通过自动装箱/拆箱加上子类型加上变量“arity”参数适用的方法
      • 确定最具体的匹配方法签名(即可以成功传递给所有其他匹配方法签名的方法签名);如果没有:编译器/歧义错误
  • 检查:所选方法是否合适?(编译时确定)

  • 方法调用的评估(运行时确定)

    • 确定运行时目标引用类型
    • 评估参数
    • 检查方法的可访问性
    • Locate方法-与编译时匹配的签名完全匹配的签名
    • 援引

  • 性能命中率

    按JLS中的文本分类:

    第1步:5% 第二步:60% 第三步:5% 第四步:30%

    第二步不仅在文本中大量出现,而且
    class Foo {
      void foo(String foo) { ... }
    }
    
    class Bar extends Foo {
      @Override
      void foo(Object foo) { ... }
    }
    class Another extends Bar {
      @Override
      void foo(Number foo) { ... }
    }
    
    class Foo {
        void foo(String foo) { ... }
    }
    
    class Bar extends Foo {
        @Override
        private void foo(String foo) { ... }
        void foo(Object foo) { ... }
    }
    class Another extends Bar {
        @Override
        private void foo(Object foo) { ... }
        void foo(Number foo) { ... }
    }
    
    class Foo{ //Super Class
    
      void foo(String string){
    
        // Your implementation here
      }
    }
    
    class Bar extends Foo{
    
      @Override
      void foo(String string){
        super(); //This method is implied when not explicitly stated in the method but the @Override annotation is present.
        // Your implementation here
      }
    
      // An overloaded method
      void foo(Object object){
        // Your implementation here
      }
    }