views:

156

answers:

8

Is there any specific reason why Java uses early binding for overloaded methods? Wouldn't it be possible to use late binding for this?

Example:

public class SomeClass {

    public void doSomething(Integer i) {
        System.out.println("INTEGER");
    }

    public void doSomething(Object o) {
        System.out.println("OBJECT");
    }

    public static void main (String[] args) {
        Object i = new Integer(2);
        Object o = new Object(); 
        SomeClass sc = new SomeClass();
        sc.doSomething(i);
        sc.doSomething(o); 
    } 
}

Prints: OBJECT OBJECT

I would rather expect: INTEGER OBJECT

+5  A: 

It is very simple. The method to use is chosen by the compiler and not the runtime system. This is what allows the type checking in the compiler to work in the first place.

So, if you stuff an Integer in an Object you must tell the compiler that you KNOW it contains an Integer, so the appropriate method can be chosen.

What you want to accomplish is normally done with methods on the object, so that "this.doSomething()" does what you want it to do.

Thorbjørn Ravn Andersen
+1 - beat me by a few seconds
aperkins
Feel free to write a better answer :)
Thorbjørn Ravn Andersen
he knows it's disambiguated at compile time, that's why he states 'early binding'. I think the question is why the java guys decided it to be this way?
Gary
@Gary, to allow static typing. This is probably _the_ feature that makes the Java language strong enough to build cathedrals (as the `new` operator provides for extremely strong mortar)
Thorbjørn Ravn Andersen
I think you can still have static typing with late-binding, they're not mutually exclusive, for instance virtual functions. To make this work like he expects it to, however, there needs to be an implementation of true runtime multimethods.
Gary
+1  A: 
void doSomething(Comparable c) {..}
void doSomething(Iterable i) {..}

class Foo implements Comparable, Iterable { ..}

doSomething(new Foo()); // which one??
Bozho
That example is problematic for statically bound method overloads too ... I think.
Stephen C
@Stephen C - yes, it is. But the OP wants things like this to work. Which they can't.
Bozho
@Stephen C the compiler will give you an error, at runtime it can only throw a RuntimeException. It's always ugly when "invalid" code causes exceptions instead of compiler errors.
josefx
+1  A: 

You see OBJECT OBJECT rather than INTEGER OBJECT because you've declared i to be an Object, not an Integer. If you do this instead:

public class SomeClass {

    public void doSomething(Integer i) {
        System.out.println("INTEGER");
    }

    public void doSomething(Object o) {
        System.out.println("OBJECT");
    }

    public static void main (String[] args) {
        Integer i = new Integer(2);
        Object o = new Object(); 
        SomeClass sc = new SomeClass();
        sc.doSomething(i);
        sc.doSomething(o); 
    } 
}

You'll get INTEGER OBJECT.

http://ideone.com/sEZrP


As Thorbjørn's answer explains, this is because the method call is disambiguated at compile time, not at run time.

Matt Ball
he knows it's disambiguated at compile time, that's why he states 'early binding'. The question is why the java guys decided it to be this way?
Gary
@Gary - I'm not so sure about that. I interpreted the question as, "Can I use late binding in Java" (which, by design, he cannot). Point taken, though.
Matt Ball
@Matt - Gary is right. I know about late/dynamic and early/static binding... well, I dont know everything about it obviously ;-)
Korgen
+1  A: 

Other people have explained the "why" better than I could.

What I will say, though, is that if you want that behaviour, you'll want to take a look at Double Dispatch, and particularly the Visitor Pattern.

Iain Galloway
+2  A: 

Is there any specific reason why Java uses early binding for overloaded methods? Wouldn't it be possible to use late binding for this?

One problem with dynamic binding of overloaded methods is that it wouldn't work if the different overloads had different return types. The runtime behavior would be harder to understand, and the application might have to deal with a new class of runtime exceptions caused by dynamic overload resolution failing.

A second problem is that selecting the method to use dynamically based on the actual argument types would be expensive. You cannot implement this by simple vtable method dispatching.

Besides, this is unnecessary since you can get dynamic binding of methods by using method overriding / polymorphism.

Stephen C
Yes, the java bytecode doesn't support function polymorphism, the functionality is provided by the compiler.
Gary
+3  A: 

It is actually late binding, not early binding. Early binding only happens for non-overrideable methods.

Given this code:

public class Test
{
        void foo()
        {
                System.out.println("foo");
        }

        final void bar()
        {
                System.out.println("bar");
        }

        void car(String s)
        {
                System.out.println("car String");
        }

        void car(Object o)
        {
                System.out.println("car Object");
        }

        static void star()
        {
                System.out.println("star");
        }

        public static void main(final String[] argv)
        {
                Test test;
                Object a;
                Object b;

                test = new Test();
                a    = "Hello";
                b    = new Object();
                test.foo();
                test.bar();
                test.car(a);
                test.car(b);
                Test.star();
        }
}

The javac I used generates this for main:

public static void main(java.lang.String[]);
  Code:
   0:   new #9; //class Test
   3:   dup
   4:   invokespecial   #10; //Method "<init>":()V
   7:   astore_1
   8:   ldc #11; //String Hello
   10:  astore_2
   11:  new #12; //class java/lang/Object
   14:  dup
   15:  invokespecial   #1; //Method java/lang/Object."<init>":()V
   18:  astore_3
   19:  aload_1
   20:  invokevirtual   #13; //Method foo:()V
   23:  aload_1
   24:  invokevirtual   #14; //Method bar:()V
   27:  aload_1
   28:  aload_2
   29:  invokevirtual   #15; //Method car:(Ljava/lang/Object;)V
   32:  aload_1
   33:  aload_3
   34:  invokevirtual   #15; //Method car:(Ljava/lang/Object;)V
   37:  invokestatic    #16; //Method star:()V
   40:  return    
}

invokevirtual means late binding, invokestatic and invokespecial means early binding.

The line:

24: invokevirtual #14; //Method bar:()V

refers to a non-overrideable method, so logically speaking it should be invokespecial. The runtime is apparently free to make the change when the class is loaded (I could be wrong on that, I haven't delved that deeply into the VM internals, but from what I read it seems that is the case).

So your question is really why doesn't java have what is called Multiple Dispatch (wikipedia link here) which is where the runtime decides what method to call based on the value in the variable instead of basing it on what the variable is declared as.

The way the compiler works is to say something like:

  • I am calling SomeClass.doSomething on a variable declared to be an Object.
  • Does SomeClass have a method called doSomething that takes an Object?
  • If yes, then output an invokevirtual call to that method.

What you are wanting is an additional step that happens at runtime (it could not happen at compile time) that says:

  • The variable is pointing at an Integer.
  • I am calling the SomeClass.doSomething method.
  • Call the best match of SomeClass.doSomething method that takes an Integer, a Number, an Object (call whichever it finds first).

Java doesn't to that at runtime, instead it simply calls the method that the compiler decided to call.

You can simulate multiple dispatch in Java like so.

TofuBeer
+2  A: 

It's possible. And even more, there is such code in standard library(class - TreeSet, author (sic!) Josh Bloch ).

In one of his lecture he says that it was mistake.

From Joshua Bloch How to Design a Good API & Why it Matters

Overload With Care

  • Avoid ambiguous overloadings
    • Multiple overloadings applicable to same actuals
    • Conservative: no two with same number of args
  • Just because you can doesn't mean you should
    • Often better to use a different name
  • If you must provide ambiguous overloadings, ensure same behavior for same arguments

    public TreeSet(Collection c); // Ignores order

    public TreeSet(SortedSet s); // Respects order

Stas
+3  A: 

It seems to me that the most obvious reason is that it allows the compiler to guarantee that there will actually be a function to be called.

Suppose Java chose the function based on the run-time type, and you wrote this:

public class MyClass
{
  public void foo(Integer i)
  {
    System.out.println("Integer");
  }
  public void foo(String s)
  {
    System.out.println("String");
  }
  public static void main(String[] args)
  {
    Object o1=new String("Hello world");
    foo(o1);
    Object o2=new Double(42);
    foo(o2);
  }
}

What's the output? The first call to foo presumably prints "String", but the second call has nowhere to go. I suppose it could generate a run-time error. This is similar to the argument of strictly-typed versus loosely-typed. If it chose the function at run time, it could be more flexible in some sense. But by choosing the function at compile time, we get the error messages at compile time rather than having to wait until run time and be sure that we have exercised every possible path with every relevant combination of data.

Jay
+1, but I think logially the compiler would still need to ensure that foo(Object o) existed for it to work, so you would not really even encounter such a case. This is similar to polymorphism where the compiler ensures that the method exists, but the appropriate one is called at runtime.
TofuBeer
@TofuBeer: But then that would require you to create functions that you KNOW will never be called. That is, if you create a foo(String) and a foo(Integer), and you know you never call them with any other runtime type, you would nevertheless still have to create foo(Object). What would it do? I suppose you could create a dummy function that just displays some sort of "should never get here" message, but at the least creating these dummy functions would be a pain, and at the worst programmers would surely get sloppy about them and create problems.
Jay