重载方法的优先级

5

我有一个名为 Element 的基类。一些其他类(比如 Label 和 Image)都继承了这个类。

现在我有一个分派类,其中包含以下方法:

public class Dispatcher {
    public static AbstractPropertyEditor<Label> createEditor(Label e) {
    ...
    }

    public static AbstractPropertyEditor<Element> createEditor(Element e) {
    ...
    }
}

如果我现在有一个Label实例(它继承自Element),并且我想将其传递给createEditor(),为什么会调用最通用的方法(第二个方法)?难道不应该调用最具体的方法(createEditor(Label e))吗?
我绝对需要带有Element参数的方法,以便“捕获”所有那些实现了Element但在此分派类中没有自己特定方法的类。
我正在使用Java 6,如何“修复”这个问题?
编辑:好吧,我必须承认这与泛型完全无关。但那是我第一次遇到它的地方。
谢谢和问候。

你将实例分配给了类型为 Label 而不是 Element 的变量? - Tom Hawtin - tackline
(而不是T extends Element,其中T恰好是Label?) - Tom Hawtin - tackline
5个回答

6

为什么不这样做:

  • 创建一个抽象类Element,提供默认的createEditor()实现。
  • 使Label重写createEditor()方法。

这样就不需要使用静态工具函数了,并且可以达到你的目标。

如果你需要Element是一个接口,那么:

  • define createEditor() as methods of Element
  • define a EditorFactory interface
  • provide DefaultEditorFactory and ListEditorFactory
  • use the appropriate factories in the implementors of Element:

    public Editor createEditor() {
         editorFactory.createEditor(this);
    }
    

具体来说,EditorFactory是在初始化期间或通过某种依赖注入实例化的。


针对您的具体问题 - 这取决于您编译的类型。如果调用createEditor(obj),它将取决于Element obj = ..还是Label obj = ..


嗯...得想一想,实现在Element和子类中创建createEditor的想法会有什么后果...起初看来,这并不像一个坏主意。在我的情况下,我可能可以这样使用它。但是,如果您仍然希望出于分离原因将那些静态实用程序类分开呢?在我的情况下,Java如何确定调用哪个方法?它总是调用最通用的方法吗?是否有任何方法可以使其调用最具体的方法? - Atmocreations
已更新我的答案。同时查看其他人的答案以获取确切的行为。 - Bozho
看到了,谢谢。嗯...问题是我不喜欢在调用方法之前将那个东西转换为标签。否则,我可以直接创建像createLabelEditor(Label l)、createImageEditor(Image i)等方法。这将使具有Element参数的方法过时。 - Atmocreations
这将使具有参数的方法过时(因为“this”将引用相同的对象)。然后,由于返回类型,特定方法与超类的方法不兼容。好吧,现在我有了某种冗余,但是当我将参数保留为虚拟参数时它可以工作。这绝不是一个美丽的解决方案。评论:您的版本将使编辑器非泛型化,这是必须的。 - Atmocreations
你可以轻松地“泛型化”它。 - Bozho
好吧,我可以这样做。但是这将导致一个方法不覆盖另一个方法,因为返回值将不同,因此不兼容。除非我保留虚拟参数。无论如何,谢谢。我认为这是一种恶劣的构造,用来解决可以解决的问题。例如,使用注释调用方法,您可以提供应调用哪个方法的信息。例如 @Call(MOST_SPECIFIC) createEditor(e);。无论如何,谢谢,我会将其关闭为已解决,即使我只是绕过了它。 - Atmocreations

2
这与泛型无关,而与方法重载有关。在Java中,调用的方法签名是在编译时确定的,而不是在运行时确定的,因此您必须在运行时检查和转换类型。
所以将这个替换为:
 Element label = getLabel();
 AbstractPropertyEditor<?> editor = createEditor(label);   

使用此方法:

 Element label = getLabel();
 AbtractPropertyEditor<?> editor;
 if(label instanceof Label) {
      editor = createEditor((Label) label);
 } else {
      editor = createEditor(label);
 }
另一种(更标准/更好的)解决方法是让createEditor(Element)方法检查类型,并使用转换调用子类型的正确重载方法。但是,如果您按照声明的方法这样做,将会有一个返回参数的问题。

感谢您的回复,但我想尽可能避免使用 instanceof。因此,这对我来说不是一个选项。 - Atmocreations
@Atmocreations,如果您不想使用超类型和子类型作为参数进行方法重载,我建议您不要这样做。这种模式不可避免地会导致instanceof和强制转换的出现。 - Yishai

1

来自Java语言规范

当调用方法(§15.12)时,在编译时使用实际参数的数量(以及任何显式类型参数)和参数的编译时类型来确定将要调用的方法的签名(§15.12.2)。如果要调用的方法是实例方法,则将使用动态方法查找(§15.12.4)在运行时确定要调用的实际方法。


谢谢。虽然这是绝对正确的,但它既不能帮助我解决当前的问题,也不能解释Java如何确定最终调用哪个方法。 - Atmocreations
@Atmocreations:我不同意,它清楚地说明在非实例方法的情况下,调用的方法将是与编译时参数类型相对应的方法。由于在您的情况下调用的是Element方法,这意味着您在编译时使用Element调用该方法。 - JRL
嗯...好的,对不起。我必须承认我还没有从那个角度去看过它。 - Atmocreations

0
这是一个重载方法的示例。 即使在运行时实际对象是Label而不是Element,调用哪个重载方法(换句话说,方法的签名)的选择并不是在运行时动态决定的。引用类型(而不是对象类型)决定了调用哪个重载方法!
示例
public class Car {    
}

public class Toyota extends Car {    
}

public class MyCar {

    public void run(Car c) {
        System.out.println("Run any Car");
    }

    public void run(Toyota t) {
        System.out.println("Run Toyota Car");
    }

    public static void main(String[] args) {
        MyCar myCar = new MyCar();

        Car c1 = new Car();
        myCar.run(c1); // Output: Run any Car

        Toyota c2 = new Toyota();
        myCar.run(c2); // Output: Run Toyota Car

        Car c3 = new Toyota();
        myCar.run(c3); // Output: Run any Car    
    }
}

所以,在你的情况下

Element obj1 = new Label();
Dispatcher.createEditor(obj); // Method with Element argument is called 
                              // as you are passing Element 

Label obj2 = new Label();
Dispatcher.createEditor(obj); // Method with Label argument is called 
                              // as you are passing Label

另外需要注意的是,重写方法的调用发生在运行时,并且取决于对象类型(换句话说,取决于堆上实际实例的类型)


0

由于你可能正在进行编程:

Element element = new Label();

这是由编译器决定的。


嗯,间接地(根据情况调用不同的方法)我正在这样做。它真的取决于变量element声明的类型来确定到底调用哪个方法吗?不是调用“真实”类型(Label)的方法吗?如果不是,原因是什么? - Atmocreations

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接