- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我最近遇到了两个重载问题,我找不到答案并且没有 java 环境来运行一些测试代码。我希望有人可以通过汇编 Java 编译器遵循的所有规则的列表来帮助我进行重载,或者交替地将我指向一个已经存在的列表。
首先,当两个方法仅在最终可变参数上有所不同时,在什么情况下会调用每个方法,您可以在没有任何参数的情况下调用可变参数方法吗?
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?
第二个问题,当两种方法因从彼此继承的类型不同而被调用时?我希望调用最派生的版本,并允许调用另一个版本。
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?
这是两个例子,但作为代码阅读者,我更喜欢用于解决此问题的准确有序规则的规范列表,因为我经常没有时间设置构建环境来检查编译器是什么做。
最佳答案
重载与覆盖
方法的正确实现的选择是在运行时完成的,正如您所指出的,现在要调用的方法的签名是在编译时决定的。
编译时重载方法选择
Java Language Specification (JLS) 第 15.12 节 Method Invocation Expressions详细解释了编译器选择正确调用方法所遵循的过程。
在那里,您会注意到这是一个编译时任务。 JLS 在第 15.12.2 小节中说:
This step uses the name of the method and the types of the argument expressions to 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.
通常情况下,可变参数方法是最后选择的,如果它们与其他候选方法竞争,因为它们被认为不如接收相同参数类型的方法具体。
要验证其编译时性质,您可以进行以下测试。
像这样声明一个类并编译它(即 javac ChooseMethod.java
)。
public class ChooseMethod {
public void doSomething(Number n){
System.out.println("Number");
}
}
声明第二个类调用第一个类的方法并编译它(即 javac MethodChooser.java
)。
public class MethodChooser {
public static void main(String[] args) {
ChooseMethod m = new ChooseMethod();
m.doSomething(10);
}
}
如果您运行该程序(即 java MethodChooser
),输出为 Number
。
现在,向 ChooseMethod
类添加第二个更具体的重载方法,并重新编译它(但不要重新编译其他类)。
public void doSomething(Integer i) {
System.out.println("Integer");
}
如果再次运行 main,输出仍然是 Number
。
基本上,因为它是在编译时决定的。如果您重新编译 MethodChooser
类(带有 main 的那个),并再次运行该程序,输出将是 Integer
。
因此,如果您想强制选择其中一个重载方法,则参数的类型必须与编译时的参数类型相对应,而不仅仅是在运行时。
在运行时覆盖方法选择
同样,方法的签名是在编译时决定的,但实际的实现是在运行时决定的。
像这样声明一个类并编译它。
public class ChooseMethodA {
public void doSomething(Number n){
System.out.println("Number A");
}
}
然后声明第二个扩展类并编译:
public class ChooseMethodB extends ChooseMethodA { }
在 MethodChooser 类中,您可以:
public class MethodChooser {
public static void main(String[] args) {
ChooseMethodA m = new ChooseMethodB();
m.doSomething(10);
}
}
如果您运行它,您会得到输出 Number A
,这没问题,因为该方法尚未在 ChooseMethodB
中被覆盖,因此调用的实现是ChooseMethodA
的那个。
现在,在 MethodChooserB
中添加一个覆盖方法:
public void doSomething(Number n){
System.out.println("Number B");
}
然后重新编译这个,然后再次运行 main 方法。
现在,您得到输出 Number B
因此,实现是在运行时选择的,不需要重新编译 MethodChooser
类。
关于Java重载规则,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10901259/
假设我有一个类,我在其中重载了运算符 == : Class A { ... public: bool operator== (const A &rhs) const; ... };
我知道你不应该使用 std::find(some_map.begin(), some_map.end()) 或 std::lower_bound,因为它会采用线性时间而不是 some_map.lowe
我正在尝试在 Haskell 中定义 Vector3 数据类型,并允许在其上使用 (+) 运算符。我尝试了以下方法: data Vector3 = Vector3 Double Double Doub
我已经为我的类图将运算符重载为“-”。它的用途并不完全直观(糟糕的编码 - 我知道)但是如果我做 graph3 = graph2-graph1 那么图 3 是应该只接收图 2 和图 1 中的那些顶点。
这个问题在这里已经有了答案: 关闭 11 年前。 Possible Duplicate: Operator overloading 我想重载 以按字母顺序排列字符串,但我不确定该怎么做。 如何再次
下面的代码给我一个编译错误。谁能告诉我为什么? class mytype { public: int value; mytype(int a) { value = a;
这有什么问题吗? class Vec2 attr_accessor :x, :y # ... def += (v) @x += v.x @y += v.y retu
是否可以重载 [] 运算符两次?允许这样的事情:function[3][3](就像在二维数组中一样)。 如果可能的话,我想看看一些示例代码。 最佳答案 您可以重载 operator[] 以返回一个对象
我的团队目前正在与 Lua 合作,创建一个 android 游戏。我们遇到的一件事是表面上无法创建重载构造函数。 我习惯于使用默认值设置一个对象,然后在需要时使其过载。 前任: apples() {
我有一个网页,在某个时候显示一个导航栏,它只不过是一个 a 元素的列表 (ul)。所述 a 元素的大多数样式规则都是通用的。唯一应该改变的部分是要显示的图像,可以从列表中每个 li 元素的 id 标签
我对使用/重载“范围步长”运算符(.. ..)很感兴趣,但我终其一生都无法了解如何使用它。 在文档中它说 // Usage: start .. step .. finish 但是在 F# shell
Java 11(可能无关紧要): public static String toString(Object obj) { return ReflectionToStringBuilder.to
就目前而言,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引起辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visit the he
我无法理解以下代码(针对行号进行注释) class Base { void m1(Object o) { } void m2(String o) { } } publi
我有以下代码片段: #include using namespace std; struct Integer{ int x; Integer(const int val) : x(v
class myclass{ //definitions here }; myclass e; int myarray[10]; /* Do something... */ e = myarray;
为什么不能将下标运算符(operator [])作为 friend 函数重载? 最佳答案 正如Bjarne Stroustrup在D&E book中所说: However, even in the o
我有以下代码片段: #include using namespace std; struct Integer{ int x; Integer(const int val) : x(v
因此,我有一个问题是我最近尝试重载 namespace Eng { /** * A structure to represent pixels */ typedef
如何重载onResume()以正确的方式工作?我想从 activity 返回到 MainActivity ,我希望在其中具有与应用程序启动后相同的状态。我想使用 recreate() 但它循环了或者类
我是一名优秀的程序员,十分优秀!