views:

132

answers:

4

Hello

I have a base-class called Element. Some other classes (like Label and Image) both extend this class.

I now have a dispatching class having the following methods:

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

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

If now I have an instance of Label (which extends Element) and I want to pass it to createEditor(), why is the most generic method (the second one) called? Wouldn't it be normal that the most specific method (createEditor(Label e)) is called?

I absolutely need the method with the Element-param in order to "catch" all those classes that a) implement Element but do not have their own specific method in this dispatching class..

I'm using Java 6, how to "fix" this?

Edit: Okay, I have to admit it's not at all about generics. But that's where I encountered it the first time.

thanks and regards

A: 

Since you are probably doing :

Element element = new Label();

It is determined by the compiler.

fastcodejava
well, seen indirectly (calling different methods depending on the situation) I'm doing this, yes. Does it really depend on the declared type of the var `element` to determine which method really is invoked? Isn't the method with the "real" type (Label) invoked? And if not, what's the reason for this?
Atmocreations
+2  A: 

This really has little to do with generics, and everything to do with method overloading. In Java, the method signature called is determined at compile time, not at runtime, so you have to check and cast at runtime.

So replace this:

 Element label = getLabel();
 AbstractPropertyEditor<?> editor = createEditor(label);   

With this:

 Element label = getLabel();
 AbtractPropertyEditor<?> editor;
 if(label instanceof Label) {
      editor = createEditor((Label) label);
 } else {
      editor = createEditor(label);
 }

The other (more standard/better) way to fix this is to have the createEditor(Element) method check the type and call with a cast the correct overloaded method for subtypes. However, you will have an issue with your return parameters if you do that on the methods as declared.

Yishai
Thanks for the good response, but I would like to avoid `instanceof` wherever possible. Therefore, this is not an option to me.
Atmocreations
@Atmocreations, then I suggest you don't use method overloading with super and subtypes as parameters. Such a pattern inevitably leads to instanceof and casting.
Yishai
+6  A: 

Why don't you:

  • make Element abstract class that provides a default createEditor() implementation
  • make Label override the createEditor().

Thus you won't need the static utilities and will achieve your goal.

If you need Element to be an interface, then:

  • 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);
    }
    

where the concrete EditorFactory is instantiated either during initialization or via some sort of dependecy-injection.


As per your concrete question - it depends on what type you have compiled there. If you call createEditor(obj) it will depend whether it's Element obj = .. or Label obj = ..

Bozho
hmmmm... gotta think a while about the consequences the idea of implementing createEditor in Element and subclasses would have... in a first instance, it doesn't sound like a bad idea. and in my situation I might be able to use it that way. But what if you still want those static utility class separate for reasons of separation? How does Java determine in my case which method to call? Does it always call the most generic one? Is there any way to have it called the most specific one?
Atmocreations
updated my answer. and see others' answers as well for the exact behaviour.
Bozho
seen it, thx. well... the problem is that I don't like to cast that thing into a Label right before calling the method. Otherwise I could directly create methods like createLabelEditor(Label l), createImageEditor(Image i) etc. This would obsolete the method having Element as parameter.
Atmocreations
This would obsolete the method having a parameter (as `this` would refer to the same object). It then would result in having the specific method incompatible to the super class' one due to the return type. Okay, I now have some kind of redundancy but it works when I keep the parameter as a dummy. Not a beautiful solution at all. Comment: your version would fore Editor to be non-generic, which is a must-have.
Atmocreations
you can easily "genericize" it
Bozho
well I could. but this would result in a method not overriding the other one because the return values would be different and therefore not compatible. Except if I keep the dummy-param. Thanks anyway. I think this is a nasty construct to work around a problem that could be solved. For example an annotation for a method call with which you can provide which method should be called. for example `@Call(MOST_SPECIFIC) createEditor(e);`.thanks anyway, I'll close it as solved, even though I've just worked around it.
Atmocreations
+1  A: 

From the Java Language Specification:

When a method is invoked (§15.12), the number of actual arguments (and any explicit type arguments) and the compile-time types of the arguments are 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).

JRL
thx. but while this is absolutely correct, it neither helps me with the current problem nor does it explain the way java determines which method is really called at the end.
Atmocreations
@Atmocreations: I beg to differ, it clearly states that in the case of non-instance methods, the invoked method will be the one that corresponds to the types of the arguments at compile-time. Since in your case that is the Element one that gets called, it means you are calling the method with an Element at compile time.
JRL
Well... right, excuse me. I must admit that I haven't seen it that way yet.
Atmocreations