【问题标题】:JAVA: Methods that have same names?JAVA:具有相同名称的方法?
【发布时间】:2013-11-16 04:23:57
【问题描述】:

在 Main 方法中,我调用了 foo(1),它打印出“foo a”。所以我不明白为什么编译器没有给我一个错误,因为这两个方法具有相同的名称。

我也尝试将它们设置为私有和公共,但仍然可以正常工作..

然后当我将“foo b”方法放在“foo a”之上时,它会打印“foo b”。那么编译器会按顺序搜索方法吗?

这是所有代码,

public class Practice{

  public static void main(String[] args) {
     foo(1);
  }


  private static void foo(int n){
    System.out.println("foo a");
  }

  public static void foo(int n){
    System.out.println("foo b");
  }

}

【问题讨论】:

  • 他们是同一班吗?
  • 尝试在课外打印。它总是会打印“foo b”。
  • 我想看看在同一个类中编译并拥有这两个方法的代码。
  • 提供足够数量的代码,包括你的 Main 类以及你是如何调用它的。
  • 好的。还有一次:您的代码无法编译。

标签: java methods overloading


【解决方案1】:

嗯。我无法编译它。

我认为这可能是与How does Java distinguish these multiple methods with the same name/signature? 类似的问题。

你是从某个地方复制粘贴了一个方法,还是你输入了它们?

也许这也是以前编译工作的保留。您是否尝试过清理您的项目(/删除可执行文件),然后重新编译/运行?

注意:代码不会正常编译。您需要在 eclipse 上忽略错误以使其编译。

嗯,它确实有效!我现在认为它一定是一个 Eclipse 怪癖,因为我仍然无法使用 javac 编译它。当您使用 eclipse 强制编译时,它必须以第一个为有效。第二个引发编译器错误,随后被 eclipse 忽略。因此,您总是打印链中第一个方法的结果。

遵循 Narendra Pathai 的建议并在编译的 .class 文件上运行 javap 确实只显示了一种 foo 方法 :) [是的,我添加了 -private 标志]。

(结果): 编译自“Practice.java”

public class Practice extends java.lang.Object{
    public Practice();
    public static void main(java.lang.String[]);
    public static void foo(int);
}

(如果您交换公私): 编译自“Practice.java”

public class Practice extends java.lang.Object{
    public Practice();
    public static void main(java.lang.String[]);
    private static void foo(int);
}

【讨论】:

  • 我输入了它们,它确实编译了。
  • 好的......所以这是一个日食“问题”,而不是我的概念错误?谢谢!
  • 这是 Eclipse 的功能,允许您这样做。你不应该有这个代码。
  • 您如何检查错误处理后生成的字节码。我敢打赌它只会包含一个 foo 方法。
  • 是的,我认为也是这样。
【解决方案2】:

编译时错误 - 方法重复

不可能有重复的方法。甚至 eclipse 也会出错。

关于为什么它仍然可以运行的答案:

Exception in thread "main" java.lang.Error: Unresolved compilation problem: 

    at Main.main(Main.java:4)

【讨论】:

  • 检查更新。我建议您暂时离开 IDE 并使用命令行对其进行编译。看看会发生什么。
  • 是的,当我在命令行中尝试时,这是一个错误。 @Alexander Lin 刚刚解释过,谢谢:D
【解决方案3】:

我无法运行此代码(我假设它们在同一个类中),它会抛出错误“方法已定义......”我认为这是正确的,因为该方法具有相同的名称和相同的参数,因此重载不适用于这里。我不认为改变公共和私人可以改变这种行为。你确定你可以正常运行这段代码吗?

【讨论】:

  • 我真的很确定它运行得很好!!
【解决方案4】:

来自docs:

您不能声明多个具有相同名称的方法并且 相同数量和类型的参数,因为编译器无法分辨 他们分开。

无论您声称什么,此代码都不会编译。再次检查!

加法(用于 roliu),来自 JLS(§8.4.2):

方法 m1 的签名是 a 的签名的子签名 方法 m2,如果:

m2 与 m1 具有相同的签名,或者

m1 的签名与签名的擦除(§4.6)相同 平方米。

两个方法签名 m1 和 m2 是覆盖等效的,只要 m1 是 m2 的子签名或 m2 是 m1 的子签名。

声明两个方法是编译时错误 覆盖类中的等效签名。

由于foo() 这两种方法具有相同的签名,因此它们是override-equivalent 这意味着,根据规范,我们应该得到一个编译错误。

更新:
JDK 版本中有a bug:5.0 和 6u21 已修复 - 但如果 OP 有旧版本的 JDK,则可能会解释该问题。

【讨论】:

  • 虽然教程文档确实很有用,但我认为在讨论语言的细节时最好参考语言规范。通俗地写,很容易误导读者。特别是,在这里,它忽略了泛型参数类型也是方法签名的一部分这一事实。
  • @roliu - 给你;)
  • 这很好,但已经有两个答案在您之前引用了 JLS(包括我的:P)。你也没有解释什么是方法签名。无论如何,看起来 OP 得到了他想要的东西,虽然我不能确切地说出他想要什么......
  • 不确定您所说的另一个选项是什么意思。对我来说似乎很明显至少发生了一件事情:1) OP 正在使用一些不兼容的编译器(这是你的错误所暗示的),2) 编译器对此很好,并且 OP 忽略了编译器警告, 3) 语言规范发生了变化,一些(非常非常非常)旧版本的 Java 在方法重载冲突方面更加宽松(可能未指定)。最终,由于 OP 没有发布该信息,我们无能为力。
  • @roliu 我指的是选项#1
【解决方案5】:

来自 JLS - 两个方法具有相同的签名 if they have the same name and argument types;参数类型确定如下:

  • 它们具有相同数量的形参
  • 它们具有相同数量的类型参数

如果有两个“覆盖等效”的方法是编译错误;也就是说,如果方法的形参具有相同的擦除,则它们是覆盖等效的。

根据我对 JLS 的阅读,可见性修饰符和/或static 不会影响方法签名。因此,此代码无法在我的机器上编译(Java 1.7. 0_45)。


用外行的话来说:把它想象成有人要求你开门。

除了,你面前有两扇门。 完全相同的门。

它们的颜色、质地、形状完全相同,门把手也一样。

它们甚至有相同的凹痕、凹痕和划痕。

他们想让你打开哪扇门?没有什么可以区分他们甚至开始猜测哪个是有效的。

这就是类型擦除的问题——如果你的方法具有相同的type erasure,并且最终是覆盖等效的,那么 Java 也面临着两扇门的问题。只是,它没有猜测它,它只是放下并要求你澄清。

【讨论】:

    【解决方案6】:

    我可以复制 OPs 问题

        public class TestDuplicate {
        private static void foo(int n) {
            System.out.println("foo a");
        }
    
        public static void foo(int n) {
            System.out.println("foo b");
        }
    
        public static void main(String[] args) {
            foo(1);
        }
    
    }
    

    out: foo a

    更多信息

     {java.runtime.name=Java(TM) SE Runtime Environment, sun.boot.library.path=C:\Program Files\Java\jre7\bin, java.vm.version=23.7-b01, user.country.format=NO, java.vm.vendor=Oracle Corporation, java.vendor.url=http://java.oracle.com/, path.separator=;, java.vm.name=Java HotSpot(TM) 64-Bit Server VM, file.encoding.pkg=sun.io, user.country=US, user.script=, sun.java.launcher=SUN_STANDARD, sun.os.patch.level=Service Pack 1, java.vm.specification.name=Java Virtual Machine Specification, user.dir=C:\Workspace\StackOverflow, java.runtime.version=1.7.0_17-b02, java.awt.graphicsenv=sun.awt.Win32GraphicsEnvironment, java.endorsed.dirs=C:\Program Files\Java\jre7\lib\endorsed, os.arch=amd64, java.io.tmpdir=C:\Users\Maki\AppData\Local\Temp\, line.separator=
    , java.vm.specification.vendor=Oracle Corporation, user.variant=, os.name=Windows 7, sun.jnu.encoding=Cp1252, java.library.path=C:\Program Files\Java\jre7\bin;C:\Windows\Sun\Java\bin;C:\Windows\system32;C:\Windows;C:\Program Files (x86)\Intel\iCLS Client\;C:\Program Files\Intel\iCLS Client\;C:\Program Files (x86)\NVIDIA Corporation\PhysX\Common;C:\Program Files (x86)\AMD APP\bin\x86_64;C:\Program Files (x86)\AMD APP\bin\x86;C:\Windows\system32;C:\Windows;C:\Windows\System32\Wbem;C:\Windows\System32\WindowsPowerShell\v1.0\;C:\Program Files (x86)\ATI Technologies\ATI.ACE\Core-Static;C:\Program Files\MiKTeX 2.9\miktex\bin\x64\;C:\Program Files\Java\jdk1.7.0_17\bin;C:\Program Files\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files\Intel\Intel(R) Management Engine Components\IPT;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\DAL;C:\Program Files (x86)\Intel\Intel(R) Management Engine Components\IPT;., java.specification.name=Java Platform API Specification, java.class.version=51.0, sun.management.compiler=HotSpot 64-Bit Tiered Compilers, os.version=6.1, user.home=C:\Users\Maki, user.timezone=, java.awt.printerjob=sun.awt.windows.WPrinterJob, file.encoding=UTF-8, java.specification.version=1.7, java.class.path=C:\Workspace\StackOverflow\bin, user.name=Maki, java.vm.specification.version=1.7, sun.java.command=TestDuplicate, java.home=C:\Program Files\Java\jre7, sun.arch.data.model=64, user.language=en, java.specification.vendor=Oracle Corporation, user.language.format=no, awt.toolkit=sun.awt.windows.WToolkit, java.vm.info=mixed mode, java.version=1.7.0_17, java.ext.dirs=C:\Program Files\Java\jre7\lib\ext;C:\Windows\Sun\Java\lib\ext, sun.boot.class.path=C:\Program Files\Java\jre7\lib\resources.jar;C:\Program Files\Java\jre7\lib\rt.jar;C:\Program Files\Java\jre7\lib\sunrsasign.jar;C:\Program Files\Java\jre7\lib\jsse.jar;C:\Program Files\Java\jre7\lib\jce.jar;C:\Program Files\Java\jre7\lib\charsets.jar;C:\Program Files\Java\jre7\lib\jfr.jar;C:\Program Files\Java\jre7\classes, java.vendor=Oracle Corporation, file.separator=\, java.vendor.url.bug=http://bugreport.sun.com/bugreport/, sun.io.unicode.encoding=UnicodeLittle, sun.cpu.endian=little, sun.desktop=windows, sun.cpu.isalist=amd64}
    

    我觉得这很奇怪,语言规范没有说明具有不同访问修饰符的重复方法。

    我的 IDE (eclipse) 抱怨方法上有红色下划线,警告我有重复的方法,但代码编译并运行得很好。

    【讨论】:

    • 发布复制此行为的整个类。
    • 语言规范确实说明了返回类型是方法描述符的一部分,并且没有两个方法可以具有相同的描述,考虑到上述情况,这很奇怪......
    • 为了在 Eclipse 下运行此代码,我必须在“所需项目中存在错误”对话框中单击“继续”。我认为一旦 Eclipse 标记了错误,并且程序员在该对话框中选择了“继续”,程序员就可以选择忽略错误。
    • 没错,但我没有得到这样的东西,这也让我感到惊讶。
    • 它不能也不会在我熟悉的任何 Java 版本下编译。例如:javac -version => javac 1.7.0_25; javac TestDuplicate.java => "TestDuplicate.java:6: 错误:方法 foo(int) 已在 TestDuplicate 类中定义"
    【解决方案7】:

    我刚刚在这里浏览了 Java 7 规范:http://docs.oracle.com/javase/specs/ 并且(隐式)描述的代码违反了规范。

    以下是相关的sn-ps:

    8.4 方法声明

    一个方法声明可以调用的可执行代码,传递一个固定的数字 值作为参数。

    方法声明: 方法头方法体

    方法头: MethodModifiers_opt TypeParameters_opt 结果 MethodDeclarator Throws_opt

    方法声明器: 标识符(FormalParameterList_opt)

    ...

    MethodDeclarator 中的标识符可以用在名称中以引用该方法。

    类的主体将两个具有重写等效签名的方法声明为成员是编译时错误(第 8.4.2 节)。

    8.4.1 形式参数

    方法或构造函数的形式参数(如果有)由逗号分隔的参数说明符列表指定。

    8.4.4 通用方法

    如果声明一个或多个类型变量(第 4.4 节),则该方法是泛型的。这些类型变量称为方法的类型参数。

    8.4.2 方法签名

    如果两个方法具有相同的名称和参数,则它们具有相同的签名 类型。如果满足以下所有条件,则两个方法或构造函数声明 M 和 N 具有相同的参数类型:

    • 它们具有相同数量的形式参数(可能为零)

    • 它们具有相同数量的类型参数(可能为零)

    • 令 A1, ..., An 为 M 的类型参数,令 B1, ..., Bn 为 N 的类型参数。将 N 的类型中出现的 Bi 重命名为 Ai 后,对应的类型变量相同,M和N的形参类型相同。

    如果满足以下任一条件,方法 m1 的签名是方法 m2 签名的子签名:

    • m2 与 m1 具有相同的签名,或者

    • m1 的签名与 m2 签名的擦除(第 4.6 节)相同。

    两个方法签名 m1 和 m2 是覆盖等效的,只要 m1 是 m2 或 m2 的子签名是 m1 的子签名。

    在一个类中声明两个具有重写等效签名的方法是编译时错误。

    它定义方法名称的方式对我来说有点模棱两可,但我认为很清楚,对签名冲突唯一重要的是:

    1. 方法名
    2. 泛型类型参数,如果有的话
    3. 输入参数

    【讨论】:

      【解决方案8】:

      它也不会在Eclipse 的外部编译。

      从命令提示符运行javac (1.7):

      Practice.java:13: error: method foo(int) is already defined in class Practice
              public static void foo(int n){
                                 ^
      1 error
      

      【讨论】:

        【解决方案9】:

        在这里,定义什么是SAME方法http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.4.2

        two methods have the same signature if they have the same name and argument types.
        

        所以你会得到一个编译时错误,也就是说:检查你的代码和 IDE

        It is a compile-time error to declare two methods with override-equivalent signatures in a class.
        

        或者你可能正在运行你以前的编译类文件

        【讨论】:

          【解决方案10】:

          这是因为公共/私人设置。私有方法不能在 obj 之外访问。但是,公共方法可以,这不是错误。

          【讨论】:

          • 那就把静态去掉..我在网上看到的所有重载方法都是public void函数..
          • @LazyFrog - 你解释它的方式,你的代码不会编译。期间。
          • -1 方法签名既不是由访问级别修饰符定义的,也不是由其静态与否这一事实定义的。
          猜你喜欢
          • 1970-01-01
          • 2011-11-23
          • 2013-06-14
          • 1970-01-01
          • 1970-01-01
          • 2015-03-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多