【问题标题】:Java - why no return type based method overloading?Java - 为什么没有基于返回类型的方法重载?
【发布时间】:2011-02-14 05:30:14
【问题描述】:

我知道这是不可能的,但是谁能提供一个理论来解释为什么 Java 选择不支持这个?我之所以问,是因为我刚刚遇到了一种情况,我认为拥有它会很好。

【问题讨论】:

    标签: java programming-languages return overloading


    【解决方案1】:

    因为在 Java 中不需要捕获方法的返回值,在这种情况下编译器无法决定使用哪个重载。例如

    boolean doSomething() { ... }
    
    int doSomething() { ... }
    
    doSomething(); // which one to call???
    

    【讨论】:

    • 有趣的是,Java 语言禁止这样做,但 JVM 没有。看我的回答...
    • @PéterTörök 但为什么继承需要相同的返回类型?为什么不隐藏概念在那里工作?在这里我发布了问题但没有答案。stackoverflow.com/questions/35957019/…
    【解决方案2】:

    关于这个问题的一个有趣的方面是 Java 语言禁止仅通过返回类型重载方法。但不是 JVM:

    注意可能不止一个 类中的匹配方法,因为 而 Java 语言禁止 声明多个方法的类 相同的签名但不同 返回类型,Java 虚拟机 才不是。这增加了灵活性 在虚拟机中可以用来 实现各种语言功能。 例如,协变收益可以是 用桥接方法实现;这 桥接法和方法是 覆盖将具有相同的 签名,但返回类型不同。

    发件人:Class.getMethod(String, Class...)

    【讨论】:

    • 是的,JVM 的深度。此外,对于泛型方法参数,有一些非常好的 JVM 技巧可以使用“合成”方法应用,这些方法在 Java 语言中也不存在……
    • @LukasEder:嗯,它们有点适用于 Java 语言,因为编译器偶尔会产生合成方法(例如返回类型协方差)。
    • @JoachimSauer:是的,你可以说...编译器使用了 JVM 功能,但用户不能明确使用该功能...
    【解决方案3】:

    我想知道为什么他们也不支持这一点。当然,如果您忽略返回值,编译器将无法知道您想要哪个。但这与传递空值产生的歧义相同。喜欢:

    String doSomething(String s) { ... }
    String doSomething(Integer s) { ... }
    ...
    String out=doSomething(null);
    

    在这种情况下,编译器只是抱怨调用不明确,您必须通过强制转换 null 来解决它,例如:

    String out=doSomething((String)null);
    

    您可以通过返回类型重载来做同样的事情:

    String getSomething() { ... }
    Integer getSomething() { ... }
    ...
    Integer n=getSomething();
    

    大概会调用第二个函数。

    getSomething();
    

    会模棱两可(在这个例子中,可能没用,除非它有副作用,但这是另一个故事),所以你不得不说:

    (String) getSomething();
    

    也许更现实一点:

    if ((String) getSomething()==null) ...
    

    但这是简单的情况。我可以看到一个编译器编写者不想支持这一点,因为除了简单的赋值之外,它可能会变得非常复杂。例如,考虑:

    String getSomething() { ... };
    Integer getSomething() { ... };
    String getOtherthing() { ... };
    ...
    if (getSomething().equals(getOtherthing())) ...
    

    编译器必须确定 String 和 Integer 都具有 equals 函数,因此此时任何一个都有效。然后它必须注意到 getOtherthing 是一个字符串,而 Integer.equals(String) 不太可能,所以作者想要的可能是 String.equals(String)。可行,但那时我开始看到,在一般情况下,这可能是一头野兽。

    然后假设我们添加:

    Integer getOtherthing() { ... };
    

    现在编译器对该 IF 语句做了什么?它可以使用两个函数的字符串版本,或整数,但不能使用一个函数的字符串和另一个函数的整数。到那时,我猜它必须坚持演员才能告诉它。但复杂性真的失控了。

    如果编译器很难弄清楚你的真正意思,想象一下对于无法像编译器一样快地查找所有函数签名的另一个程序员会是什么样子。

    【讨论】:

    • 如果我正在设计语言/框架,我会要求所有重载,但任何签名的重载都必须标记为“次要”;仅当函数的返回值通过分配给特定类型显式强制转换或强制时,才会考虑此类重载,或者 - 对于返回 void 的二级重载 - 如果它会被完全忽略。在将返回值作为参数传递给其他函数的情况下,不会考虑二次重载。我认为这样的规则会避免歧义,同时允许一些有用的优化。
    • 你还是会遇到麻烦。如果我们有“primary String doX”和“secondary Integer doX”,然后还有“primary Integer doY”和“secondary String doY”怎么办?然后我们写“if (dox().equals(doY())”。我们是使用 doX 的主要版本,因此两个函数的字符串版本,还是 doY 的主要版本,因此两个函数的整数版本?最终你最终会出现你必须以某种方式指定你的意思的情况——比如说“doX%String%()”或类似的语法——和/或必须有一些任意且可能令人困惑的规则被选中了。
    【解决方案4】:

    这是因为你可以随意忽略返回值。

    【讨论】:

      【解决方案5】:

      虽然理论上是可行的,但它并没有在 Java 中使用,原因与它没有在 C++ 中使用的原因相同;也就是说,已经发现基于返回类型的重载通常更容易让开发人员感到困惑,与实现它的成本相比,收益是微不足道的,并且在没有将返回类型分配给价值。由于这些原因,不支持基于返回类型的重载。

      【讨论】:

      • 让开发人员和编译器感到困惑,尤其是在需要返回值的上下文中不使用调用时。
      【解决方案6】:

      我认为原因之一是,在大多数情况下,您只能在函数执行之后而不是在此过程之前确定函数的返回类型。因此,仅根据函数的返回类型不同,无法帮助您决定调用哪个重载函数。

      【讨论】:

      • 这在动态语言中是一个很好的理由,但是在 Java 中,要调用的方法签名(包括返回类型)在编译时被解析,所以由于编译器可以理解返回类型,它可以理论上解决这个问题(如果不是其他答案中的问题)。
      【解决方案7】:

      仅根据返回类型重载函数会使编译器混淆调用之间的区别,因为在调用时您没有给出返回类型,就像参数的情况一样,所以您只给出参数列表和这个参数列表是编译器区分函数调用的唯一方法,并且在函数完成时接收返回值,这就是为什么返回类型对函数的签名没有贡献

      【讨论】:

        【解决方案8】:

        方法重载使用编译时多态技术。 方法覆盖使用运行时多态技术。

        方法重载:

        正如前面的答案所说,java 不支持具有不同返回类型和相同参数的方法重载。这是因为,它必须在编译时自行确定使用哪种方法。为了消除歧义,他们设计了这样的方法重载技术。

        如果我们真的需要不同类型和相同参数的方法重载,在某种程度上可以使用方法覆盖。

        方法覆盖:

        方法覆盖使用运行时多态技术。因此,执行哪种方法不是在编译时决定的,而是在运行时由 JVM 决定的。

        使用协变返回类型,我们可以在子类的方法中使用与基类相同的参数有不同的返回类型。

        协变返回类型示例:

                    class BaseClass {
                        BaseClass dosomething(){
                            System.out.println("Print BaseClass");
                            return this;
                        }
        
                    }
        
                    class AnotherBaseClass extends BaseClass {
                        @Override
                        BaseClass dosomething(){
                            System.out.println("Print AnotherBaseClass");
                            return this;
                        }
                    }
        
        
        
                    class SubClass extends AnotherBaseClass {
                        @Override
                        SubClass dosomething(){ /*Here SubClass is co-vairantreturn type*/ 
                            System.out.println("Print SubClass");
                            return this;
                        }
                        public static void main(String args[]){
                          SubClass s1 = new SubClass();
                          s1.dosomething();
                        }
                    }
        

        输出:

        打印子类

        【讨论】:

          【解决方案9】:

          因为Java可以将返回类型值强制转换为目标变量,所以它不知道你将函数的返回值存储成什么变量类型。

          【讨论】:

            猜你喜欢
            • 2010-12-07
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2015-06-10
            • 1970-01-01
            • 2018-01-26
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多