我试图理解为什么下面的代码总是调用" out(Object)"方法而不是更具体的" out(int | String)"方法。非常感谢解释和解决方法。
public static void main(String[] args) {
Object[] objs = new Object[]{1, 2, 3, "test1", "test2", "test3", 1000L};
for (Object o : objs) {
out(o.getClass().cast(o)); // I have also tried passing 'o' directly
// rather than casting, but that still results
// in the out(Object) method being called
}
}
private static void out(int value) {
System.out.println("integer: " + value);
}
private static void out(String value) {
System.out.println("string : " + value);
}
private static void out(Object value) {
System.out.println("object : " + value);
}
答案 0 :(得分:5)
重载解析始终在编译时发生。编译时类型o
以及cast()
方法的结果是Object
,因此它会调用该重载。
答案 1 :(得分:1)
使用反射允许在运行时确定方法。虽然我需要将方法更改为public而不是private,但int方法需要更改为Integer。如果使用它也要确保更改类的名称"测试"到你班级的名字。
public static void main(String[] args) throws SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Object[] objs = new Object[]{1, 2, 3, "test1", "test2", "test3", 1000L};
for (Object o : objs) {
try {
Method m = Test.class.getMethod("out", o.getClass()); // Change Test to NameOfYourClass
m.invoke(null, o);
} catch (NoSuchMethodException e) {
out(o);
}
}
}
public static void out(Integer value) {
System.out.println("integer: " + value);
}
public static void out(String value) {
System.out.println("string : " + value);
}
public static void out(Object value) {
System.out.println("object : " + value);
}
答案 2 :(得分:1)
您正在定义一个Object数组。其中的一切都是一个对象,包括1,2,3,我假设你想成为原始类型。 1,2和3存储为整数,并且需要在方法调用(例如int
中为每种类型转换字符串等字符串时强制转换为out((int)o);
。此外,没有方法可以打印Long
,我认为这是1000L与long
的对比。
This回答应提供一些见解。可悲的是,没有办法动态投射。最简单的解决方法是将每种类型存储在自己的阵列中。
另一种解决方法是:
if(o instanceof String)
out((String) o);
else if(o instanceof Integer)
out((int) o);
else if(o instanceof Long)
out(o);
哪个会变得很麻烦。
答案 3 :(得分:1)
重载在编译时工作。因此,确定了根据参数类的实际方法。
如果你想在运行时调度,你需要将派遣的参数放到正确的位置。
考虑:
foo.bar( baz, quux );
// ^ ^ ^ ^
// | | | |
// | | \ /
// | | \/
// | | overload on the classes of these arguments
// | |
// | method name
// |
// dispatch on this “argument”
从概念上讲,方法名称和(重载)参数构成了一个在编译时修复的“消息”结构。然后将该消息“发送”到运行时找到的对象。
为了在Java中获得调度效果,通常会使用访客。
class PrintMyObjectsVisitor implements MyObjectsVisitor {
public void visit (MyInteger i) {
System.out.println( "Integer: " + i.get() );
}
public void visit (MyString s) {
System.out.println( "String: " + s.get() );
}
public void visit (MyObject o) {
System.out.println( "Object: " + o.get() );
}
}
,其中
interface MyObjectsVisitor {
void visit (MyInteger i);
void visit (MyString s);
void visit (MyObject o);
}
为了便于访问,您需要为对象提供合适的accept
方法。由于您无法扩展String或Integer,因此需要包装器。
abstract class MyObject {
abstract Object get ();
accept (MyObjectsVisitor v) {
v.visit( this );
}
}
class MyInteger extends MyObject {
private int value;
MyInteger (int value) {
this.value = value;
}
Object get () {
return Integer.valueOf(this.value);
}
accept (MyObjectsVisitor v) {
v.visit( this );
}
}
class MyString extends MyObject {
private String value;
MyString (String value) {
this.value = value;
}
Object get () {
return this.value;
}
accept (MyObjectsVisitor v) {
v.visit( this );
}
}
class MyLong extends MyObject {
private long value;
MyLong (long value) {
this.value = value;
}
Object get () {
return Long.valueOf(this.value);
}
}
然后用法如下:
class MyMain {
public static void main (String[] args) {
MyObject[] objects = new MyObject[] {new MyInteger(1),
new MyInteger(2),
new MyInteger(3),
new MyString("test1"),
new MyString("test2"),
new MyString("test3"),
new MyLong(4L)};
for (MyObject o: objects) {
o.accept( new PrintMyObjectsVisitor() );
}
}
}
如您所见,o
现在处于调度位置。编译所有accept
类的MyObject
方法以调用给定访问者的正确重载visit
方法。请注意,要实现此功能,我需要在访问者中实际实现的accept
的每个子类中重复MyObject
方法。 (如果你想知道,我没有在这里使用泛型来降低复杂性。)
另请注意,您可以实现其他MyObjectsVisitor
,而无需进一步扩展已定义的类。
这在其他语言中看起来有根本的不同。
例如,Common Lisp不使用重载。方法不附加到类,而是附加到泛型函数。您可以调度泛型函数的任何和所有必需参数。
(defun dispatch-demo ()
"A demo function to show dispatch."
(let ((objects (list 1
2
3
"test1"
"test2"
"test3"
3.14159265)))
(dolist (object objects)
(print-with-class object))))
(defgeneric print-with-class (object)
(:documentation "Prints the given object with its recognized class.
Recognized classes are defined by the methods of this generic
function."))
(defmethod print-with-class ((object string))
(format t "String: ~a~%" object))
(defmethod print-with-class ((object integer))
(format t "Integer: ~a~%" object))
(defmethod print-with-class ((object t))
(format t "Object: ~a~%" object))
在Erlang或OCaml等语言中,您可以通过模式匹配获得类似的效果。