java Java重载规则

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/10901259/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-31 03:02:13  来源:igfitidea点击:

Java overloading rules

javaoverloading

提问by Peter Oehlert

I came across two overloading questions recently that I can't find an answer for and don't have a java environment to run some test code. I'm hoping someone can help me by assembling a list of all the rules that java compilers follow follow for overloading or alternately pointing me at a list that already exists.

我最近遇到了两个重载问题,我找不到答案,也没有 Java 环境来运行一些测试代码。我希望有人可以帮助我组装 Java 编译器遵循的所有规则的列表,以便重载或交替地将我指向一个已经存在的列表。

First, when two methods differ only by a final varargs parameter, under what circumstances do each get called and can you call the varargs method without any args?

首先,当两个方法的区别仅在于最后一个 varargs 参数时,在什么情况下每个方法都会被调用并且您可以在没有任何 args 的情况下调用 varargs 方法?

private void f(int a) { /* ... */ }
private void f(int a, int... b) { /* ... */ }

f(12); // calls the former? I would expect it to
f(12, (int[])null); // calls latter, but passes null for b? 
  // Can I force the compiler to call the second method in the same fashion
  // as would happen if the first method didn't exist?

Second question, when two methods differ by types inherited from one another which gets called? I'd expect the most derived version to be called, and casting allowed to call the other.

第二个问题,当两种方法因彼此继承的类型不同而被调用时?我希望调用最派生的版本,并且允许强制转换来调用另一个版本。

interface A {}
class B implements A {}
class C implements A {}

private void f(A a) {}
private void f(B b) {}

f(new C()); // calls the first method
f(new B()); // calls the second method?
f((A)(new B()); // calls the first method using a B object?

These are the two examples, but as a code-reader I'd prefer a canonical list of the exact ordered rules used for resolving this as I frequently don't have time to setup a build environment to check what the compiler is doing.

这是两个示例,但作为代码阅读器,我更喜欢用于解决此问题的确切有序规则的规范列表,因为我经常没有时间设置构建环境来检查编译器正在做什么。

回答by Edwin Dalorzo

Overloading vs Overriding

重载与覆盖

The selection of the right implementationof method is done at runtime as you well pointed out, now the signature of the method to be invoked is decided at compile time.

正如您所指出的那样,方法的正确实现的选择是在运行时完成的,现在要调用的方法的签名是在编译时决定的。

Overloading Method Selection at Compile Time

编译时重载方法选择

The Java Language Specification(JLS) in section 15.12 Method Invocation Expressionsexplains in detail the process that the compiler follows to choose the right method to invoke.

Java语言规范(JLS)在第15.12方法调用表达式详细解释,编译器如下选择来调用正确的方法的过程。

There, you will notice that this is a compile-timetask. The JLS says in subsection 15.12.2:

在那里,您会注意到这是一个编译时任务。JLS 在第 15.12.2 小节中说:

This step uses the name of the methodand the types of the argument expressionsto locate methods that are both accessible and applicable There may be more than one such method, in which case the most specific one is chosen.

这一步使用方法名称参数表达式的类型来定位既可访问又适用的方法。这样的方法可能不止一种,在这种情况下,选择最具体的一种。

Typically, varargs methods are the last chosen, if they compete with other candidate methods, because they are considered less specific than the ones receiving the same parameter type.

通常,可变参数方法是最后选择的,如果它们与其他候选方法竞争,因为它们被认为不如接收相同参数类型的方法具体。

To verify the compile-time nature of this, you can do the following test.

要验证此的编译时性质,您可以执行以下测试。

Declare a class like this and compile it (i.e. javac ChooseMethod.java).

声明一个这样的类并编译它(即javac ChooseMethod.java)。

public class ChooseMethod {
   public void doSomething(Number n){
    System.out.println("Number");
   }
}

Declare a second class that invokes a method of the first one and compile it (i.e. javac MethodChooser.java).

声明调用第一个方法的第二个类并编译它(即javac MethodChooser.java)。

public class MethodChooser {
   public static void main(String[] args) {
    ChooseMethod m = new ChooseMethod();
    m.doSomething(10);
   }
}

If you run the program (i.e. java MethodChooser), the output says Number.

如果您运行该程序(即java MethodChooser),输出将显示Number

Now, add a second more specific overloadedmethod to the ChooseMethodclass, and recompile it (but do not recompile the other class).

现在,向该类添加第二个更具体的重载方法ChooseMethod,并重新编译它(但不要重新编译另一个类)。

public void doSomething(Integer i) {
 System.out.println("Integer");
}

If you run the main again, the output is still Number.

如果再次运行 main,输出仍然是Number.

Basically, because it was decided at compile time. If you recompile the MethodChooserclass (the one with the main), and run the program again, the output will be Integer.

基本上,因为它是在编译时决定的。如果您重新编译MethodChooser该类(带有 main 的类),并再次运行该程序,输出将为Integer.

As such, if you want to force the selection of one of the overloaded methods, the type of the arguments must correspond with the type of the parameters at compile time, and not only at run time.

因此,如果要强制选择重载方法之一,则参数类型必须在编译时与参数类型相对应,而不仅仅是在运行时。

Overriding Method Selection at Run time

在运行时覆盖方法选择

Again, the signature of the method is decided at compile time, but the actual implementation is decided at runtime.

同样,方法的签名是在编译时决定的,但实际的实现是在运行时决定的。

Declare a class like this and compile it.

声明一个这样的类并编译它。

public class ChooseMethodA {
   public void doSomething(Number n){
    System.out.println("Number A");
   }
}

Then declare a second extending class and compile:

然后声明第二个扩展类并编译:

public class ChooseMethodB extends ChooseMethodA {  }

And in the MethodChooser class you do:

在 MethodChooser 类中,您执行以下操作:

public class MethodChooser {
    public static void main(String[] args) {
        ChooseMethodA m = new ChooseMethodB();
        m.doSomething(10);
    }
}

And if you run it you get the output Number A, and this is Ok, because the method has not been overriden in ChooseMethodBand therefore the implementation being invoked is that of ChooseMethodA.

如果你运行它,你会得到输出Number A,这是好的,因为该方法没有被覆盖ChooseMethodB,因此被调用的实现是ChooseMethodA.

Now, add an overriden method in MethodChooserB:

现在,在 中添加一个覆盖方法MethodChooserB

public void doSomething(Number n){
    System.out.println("Number B");
}

And recompile just this one, and run the main method again.

并重新编译这个,并再次运行 main 方法。

Now, you get the output Number B

现在,你得到输出 Number B

As such, the implementation was chosen at runtime, and not recompilation of the MethodChooserclass was required.

因此,实现是在运行时选择的,不需要重新编译MethodChooser类。

回答by gobernador

First question:

第一个问题:

Your assumption is correct. The second call to f()will call the varargs method. You can get the compiler to call the second method with:

你的假设是正确的。第二次调用f()将调用 varargs 方法。您可以让编译器调用第二种方法:

private void f(int a) {
    f(a, null);
}

Second question:

第二个问题:

Yes. However, you can't extend an interface. If you change Ato an abstract class, things will compile.

是的。但是,您不能扩展接口。如果您更改A为抽象类,事情就会编译。