为什么要调用这个重载方法而不是更具体?

时间:2014-10-05 19:54:00

标签: java overloading

我试图理解为什么下面的代码总是调用" 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);
}

4 个答案:

答案 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等语言中,您可以通过模式匹配获得类似的效果。