Overloading in Java and multiple dispatch-Collection of common programming errors


  • Vitalij Zadneprovskij

    I have a collection (or list or array list) in which I want to put both String values and double values. I decided to make it a collection of objects and using overloading ond polymorphism, but I did something wrong.

    I run a little test:

    public class OOP {
        void prova(Object o){
            System.out.println("object");
        }
    
        void prova(Integer i){
        System.out.println("integer");
        }
    
        void prova(String s){
            System.out.println("string");
        }
    
        void test(){
            Object o = new String("  ");
            this.prova(o); // Prints 'object'!!! Why?!?!?
        }
    
        public static void main(String[] args) {
            OOP oop = new OOP();
            oop.test(); // Prints 'object'!!! Why?!?!?
        }
    }
    

    In the test seems like the argument type is decided at compile time and not at runtime. Why is that?

    This question is related to:

    Polymorphism vs Overriding vs Overloading
    Try to describe polymorphism as easy as you can

    EDIT:

    Ok the method to be called is decided at compile time. Is there a workaround to avoid using the instanceof operator?


  • DaoWen

    This post seconds voo’s answer, and gives details about/alternatives to late binding.

    General JVMs only use single dispatch: the runtime type is only considered for the receiver object; for the method’s parameters, the static type is considered. An efficient implementation with optimizations is quite easy using method tables (which are similar to C++’s virtual tables). You can find details e.g. in the HotSpot Wiki.

    If you want multiple dispatch for your parameters, take a look at

    • groovy. But to my latest knowledge, that has an outdated, slow multiple dispatch implementation (see e.g. this performance comparison), e.g. without caching.
    • clojure, but that is quite different to Java.
    • MultiJava, which offers multiple dispatch for Java. Additionally, you can use
      • this.resend(...) instead of super(...) to invoke the most-specific overridden method of the enclosing method;
      • value dispatching (code example below).

    If you want to stick with Java, you can

    • redesign your application by moving overloaded methods over a finer grained class hierarchy. An example is given in Josh Bloch’s Effective Java, Item 41 (Use overloading judiciously);
    • use some design patterns, such as Strategy, Visitor, Observer. These can often solve the same problems as multiple dispatch (i.e. in those situations you have trivial solutions for those patterns using multiple dispatch).

    Value dispatching:

    class C {
      static final int INITIALIZED = 0;
      static final int RUNNING = 1;
      static final int STOPPED = 2;
      void m(int i) {
        // the default method
      }
      void m(int@@INITIALIZED i) {
        // handle the case when we're in the initialized `state'
      }
      void m(int@@RUNNING i) {
        // handle the case when we're in the running `state'
      }
      void m(int@@STOPPED i) {
        // handle the case when we're in the stopped `state'
      }
    }
    

  • Voo

    What you want is double or more general multiple dispatch, something that is actually implemented in other languages (common lisp comes to mind)

    Presumably the main reason java doesn’t have it, is because it comes at a performance penalty because overload resolution has to be done at runtime and not compile time. The usual way around this is the visitor pattern – pretty ugly, but that’s how it is.


  • unholysampler

    When calling a method that is overloaded, Java picks the most restrictive type based on the type of the variable passed to the function. It does not use the type of the actual instance.


  • NimChimpsky

    this isn’t polymoprhism, you’ve simply overloaded a method and called it with parameter of object type


  • nist

    Everything in java is a Objekt (without the primitives). You store the strings and integers as object and then as you call the prove methode they are still refered to as objects. You should have a look at the instanceof keyword. Check this link

    void prova(Object o){
       if (o instanceof String)
        System.out.println("String");
       ....
    }