Java 基于参数真实类型的重载方法选择
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1572322/
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
Overloaded method selection based on the parameter's real type
提问by Sergey Mikhanov
I'm experimenting with this code:
我正在试验这个代码:
interface Callee {
public void foo(Object o);
public void foo(String s);
public void foo(Integer i);
}
class CalleeImpl implements Callee
public void foo(Object o) {
logger.debug("foo(Object o)");
}
public void foo(String s) {
logger.debug("foo(\"" + s + "\")");
}
public void foo(Integer i) {
logger.debug("foo(" + i + ")");
}
}
Callee callee = new CalleeImpl();
Object i = new Integer(12);
Object s = "foobar";
Object o = new Object();
callee.foo(i);
callee.foo(s);
callee.foo(o);
This prints foo(Object o)
three times. I expect the method selection to take in consideration the real (not the declared) parameter type. Am I missing something? Is there a way to modify this code so that it'll print foo(12)
, foo("foobar")
and foo(Object o)
?
这将打印foo(Object o)
三遍。我希望方法选择考虑真实的(不是声明的)参数类型。我错过了什么吗?有没有办法修改此代码,使其打印foo(12)
,foo("foobar")
和foo(Object o)
?
采纳答案by Michael Borgwardt
I expect the method selection to take in consideration the real (not the declared) parameter type. Am I missing something?
我希望方法选择考虑真实的(不是声明的)参数类型。我错过了什么吗?
Yes. Your expectation is wrong. In Java, dynamic method dispatch happens only for the object the method is called on, not for the parameter types of overloaded methods.
是的。你的期望是错误的。在 Java 中,动态方法分派仅针对调用该方法的对象发生,而不针对重载方法的参数类型。
Citing the Java Language Specification:
引用Java 语言规范:
When a method is invoked (§15.12), the number of actual arguments (and any explicit type arguments) and the compile-time types of the argumentsare used, at compile time, to determine the signature of the method that will be invoked (§15.12.2). If the method that is to be invoked is an instance method, the actual method to be invoked will be determined at run time, using dynamic method lookup (§15.12.4).
调用方法时(第 15.12 节), 在编译时使用实际参数(和任何显式类型参数)的数量和参数的编译时类型来确定将被调用的方法的签名(第 15.12.2 节)。如果要调用的方法是实例方法,则将在运行时使用动态方法查找(第 15.12.4 节)确定要调用的实际方法。
回答by Anton Gogolev
Ability to dispatch a call to a method based on types of arguments is called multiple dispatch. In Java this is done with Visitor pattern.
根据参数类型分派对方法的调用的能力称为多分派。在 Java 中,这是通过访问者模式完成的。
However, since you're dealing with Integer
s and String
s, you cannot easily incorporate this pattern (you just cannot modify these classes). Thus, a giant switch
on object run-time will be your weapon of choice.
但是,由于您正在处理Integer
s 和String
s,因此无法轻松合并此模式(您只是无法修改这些类)。因此,switch
对象运行时的巨人将是您选择的武器。
回答by Yishai
In Java the method to call (as in which method signature to use) is determined at compile time, so it goes with the compile time type.
在 Java 中,要调用的方法(如要使用的方法签名)是在编译时确定的,因此它与编译时类型一致。
The typical pattern for working around this is to check the object type in the method with the Object signature and delegate to the method with a cast.
解决此问题的典型模式是使用 Object 签名检查方法中的对象类型,并使用强制转换委托给该方法。
public void foo(Object o) {
if (o instanceof String) foo((String) o);
if (o instanceof Integer) foo((Integer) o);
logger.debug("foo(Object o)");
}
If you have many types and this is unmanageable, then method overloading is probably not the right approach, rather the public method should just take Object and implement some kind of strategy pattern to delegate the appropriate handling per object type.
如果您有很多类型并且这是无法管理的,那么方法重载可能不是正确的方法,而是公共方法应该只采用 Object 并实现某种策略模式来委派每个对象类型的适当处理。
回答by akf
Java looks at the reference type when trying to determine which method to call. If you want to force your code you choose the 'right' method, you can declare your fields as instances of the specific type:
Java 在尝试确定要调用的方法时会查看引用类型。如果您想强制您的代码选择“正确”方法,您可以将您的字段声明为特定类型的实例:
Integeri = new Integer(12);
String s = "foobar";
Object o = new Object();
You could also cast your params as the type of the param:
您还可以将您的参数转换为参数的类型:
callee.foo(i);
callee.foo((String)s);
callee.foo(((Integer)o);
回答by denis.zhdanov
As mentioned before overloading resolution is performed at compile time.
如前所述,重载解析是在编译时执行的。
Java Puzzlershas a nice example for that:
Java Puzzlers有一个很好的例子:
Puzzle 46: The Case of the Confusing Constructor
谜题46:混淆构造函数的案例
This puzzle presents you with two Confusing constructors. The main method invokes a constructor, but which one? The program's output depends on the answer. What does the program print, or is it even legal?
这个难题向您展示了两个令人困惑的构造函数。main 方法调用了一个构造函数,但是哪个呢?程序的输出取决于答案。该程序打印什么,或者它是否合法?
public class Confusing {
private Confusing(Object o) {
System.out.println("Object");
}
private Confusing(double[] dArray) {
System.out.println("double array");
}
public static void main(String[] args) {
new Confusing(null);
}
}
Solution 46: Case of the Confusing Constructor
解决方案 46:令人困惑的构造函数的案例
... Java's overload resolution process operates in two phases. The first phase selects all the methods or constructors that are accessible and applicable. The second phase selects the most specificof the methods or constructors selected in the first phase. One method or constructor is less specificthan another if it can accept any parameters passed to the other [JLS 15.12.2.5].
... Java 的重载解析过程分为两个阶段。第一阶段选择所有可访问和适用的方法或构造函数。第二阶段选择在第一阶段选择的最具体的方法或构造函数。如果一个方法或构造函数可以接受传递给另一个 [JLS 15.12.2.5] 的任何参数,它就没有另一个方法或构造函数那么具体。
In our program, both constructors are accessible and applicable. The constructor Confusing(Object)accepts any parameter passed to Confusing(double[]), so Confusing(Object)is less specific. (Every double arrayis an Object, but not every Objectis a double array.) The most specific constructor is therefore Confusing(double[]), which explains the program's output.
在我们的程序中,两个构造函数都是可访问和适用的。构造函数 Confusing(Object)接受传递给Confusing(double[]) 的任何参数,因此 Confusing(Object)不太具体。(每个双数组都是一个Object,但不是每个Object都是一个双数组。)因此,最具体的构造函数是Confusing(double[]),它解释了程序的输出。
This behavior makes sense if you pass a value of type double[]; it is counterintuitive if you pass null. The key to understanding this puzzle is that the test for which method or constructor is most specific does not use the actual parameters: the parameters appearing in the invocation. They are used only to determine which overloadings are applicable. Once the compiler determines which overloadings are applicable and accessible, it selects the most specific overloading, using only the formal parameters: the parameters appearing in the declaration.
如果您传递double[]类型的值,则此行为是有意义的;如果您传递null ,这是违反直觉的。理解这个谜题的关键是,测试哪个方法或构造函数最具体不使用实际参数:调用中出现的参数。它们仅用于确定哪些重载适用。一旦编译器确定哪些重载适用和可访问,它就会选择最具体的重载,只使用形式参数:出现在声明中的参数。
To invoke the Confusing(Object)constructor with a nullparameter, write new Confusing((Object)null). This ensures that only Confusing(Object)is applicable. More generally, to force the compiler to select a specific overloading, cast actual parameters to the declared types of the formal parameters.
要使用空参数调用Confusing(Object)构造函数,请编写new Confusing((Object)null)。这确保只有Confusing(Object)适用。更一般地,为了强制编译器选择特定的重载,将实际参数强制转换为形式参数的声明类型。
回答by Alex Worden
I had a similar issue with calling the right constructor of a class called "Parameter" that could take several basic Java types such as String, Integer, Boolean, Long, etc. Given an array of Objects, I want to convert them into an array of my Parameter objects by calling the most-specific constructor for each Object in the input array. I also wanted to define the constructor Parameter(Object o) that would throw an IllegalArgumentException. I of course found this method being invoked for every Object in my array.
我在调用名为“Parameter”的类的正确构造函数时遇到了类似的问题,该类可以采用几种基本的 Java 类型,例如 String、Integer、Boolean、Long 等。给定一个对象数组,我想将它们转换为一个数组通过为输入数组中的每个对象调用最具体的构造函数,我的 Parameter 对象。我还想定义会抛出 IllegalArgumentException 的构造函数 Parameter(Object o)。我当然发现我的数组中的每个对象都会调用这个方法。
The solution I used was to look up the constructor via reflection...
我使用的解决方案是通过反射查找构造函数......
public Parameter[] convertObjectsToParameters(Object[] objArray) {
Parameter[] paramArray = new Parameter[objArray.length];
int i = 0;
for (Object obj : objArray) {
try {
Constructor<Parameter> cons = Parameter.class.getConstructor(obj.getClass());
paramArray[i++] = cons.newInstance(obj);
} catch (Exception e) {
throw new IllegalArgumentException("This method can't handle objects of type: " + obj.getClass(), e);
}
}
return paramArray;
}
No ugly instanceof, switch statements, or visitor pattern required! :)
不需要丑陋的 instanceof、switch 语句或访问者模式!:)
回答by Ashish Thukral
If there is an exact match between the number and types of arguments specified in the method call and the method signature of an overloaded method then that is the method that will be invoked. You are using Object references, so java decides at compile time that for Object param, there is a method which accepts directly Object. So it called that method 3 times.
如果在方法调用中指定的参数数量和类型与重载方法的方法签名之间存在精确匹配,则该方法将被调用。您正在使用对象引用,因此 java 在编译时决定对于 Object 参数,有一个直接接受 Object. 所以它调用了那个方法 3 次。