17

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

Korgen
  • 5,191
  • 1
  • 29
  • 43
  • 5
    Are you asking this from a language design perspective or a 'I want to get this working' perspective? – gtrak Oct 07 '10 at 16:18
  • take a look at http://multijava.sourceforge.net/ and http://en.wikipedia.org/wiki/Multiple_dispatch – gtrak Oct 07 '10 at 16:32
  • Related: http://stackoverflow.com/questions/1572322/overloaded-method-selection-based-on-the-parameters-real-type – Flow Mar 21 '14 at 09:32
  • why just we don't use type casting? like `sc.doSomething((Integer)(i));` – Rajan Jul 22 '14 at 13:43

9 Answers9

15

Korgen, I agree with you that such feature could potentially be quite useful, which is why I found this question very interesting.

To be clear; When executing this snippet of code:

Object i = new Integer(2);
SomeClass sc = new SomeClass();
sc.doSomething(i);

the JVM would be supposed to see that i is of runtime type Integer and execute the code as if it had executed the following:

Integer i = new Integer(2);         // Notice the different static type.
SomeClass sc = new SomeClass();
sc.doSomething(i);

(Note that I'm not going into how this should be done internally, I'm just pinning down the hypothetical semantics desired in this particular situation.)

Background: (Korgen, you can skip this) Selecting which function to call is sometimes referred to as "dispatch". When taking a single type into account (the type of o in o.m()) when deciding which function to call, it is called single dispatch. What we're after here is called multiple dispatch since we would decide which function to call based on multiple types (i.e. both the runtime type of the callee and the runtime type of the arguments).

Possible reason for not including support for multiple dispatch in Java:

  1. Efficiency. Single dispatch can be much more efficiently implemented by means of a virtual method table. To quote the Wikipedia article on Double dispatch:

    In a language supporting double dispatch, this is slightly more costly, because the compiler must generate code to calculate the method's offset in the method table at runtime, thereby increasing the overall instruction path length.

  2. Legacy. This is the behavior in languages that have inspired Java, such as C++, Smalltalk and Eiffel. At the very least, single dispatch follows the principle of least astonishment.

  3. Complexity. The specification on how to determine which method to call is a quite interesting read. It is amazingly complex, and how to push the complexity over from the compiler to the JVM is by no means obvious. Consider for example the following snippet:

    class A {                          .--------------.
        void foo(Object o) {}          |      A       |
    }                                  '--------------'
                                              |
    class B extends A {                .--------------.
        void foo(Integer i) {}         |      B       |
    }                                  '--------------'
                                              |
    class C extends B {                .--------------.
        void foo(Number n) {}          |      C       |
    }                                  '--------------'
    

    now which method should be called here:

    A c = new C();
    Object i = new Integer(0);
    c.foo(i);
    

    According to the runtime type of the callee C.foo should be called while according to the runtime type of the argument B.foo should be called.

    One option would be to resolve this the same way as a call staticMethod(c, i) would be resolved in the presence of staticMethod(A, Object), staticMethod(B, Integer) and staticMethod(C, Number). (Note however that in this case neighter B.foo nor C.foo would be called, as suggested above.)

    Another option would be to choose the method primarily based on the type of the callee and secondarily based on the types of the arguments, in which case C.foo would be called.

    I'm not saying that it is impossible to pin down a well defined semantics, but I it would arguably make the rules even more complex, and possibly even counter-intuitive in some aspects. In the case of early binding, at least the compiler and/or IDE can aid the developer by giving guarantees of what will actually happen in runtime.

aioobe
  • 413,195
  • 112
  • 811
  • 826
8

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
  • 26,876
  • 10
  • 61
  • 112
  • 1
    +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 Oct 08 '10 at 14:33
  • @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 Oct 08 '10 at 15:35
  • But if you have `foo(new Object())` as well, the dummy function *would* be called. In such case you wouldn't have any unnecessary functions. Still the JVM will choose `foo(Object o)` method for *all* of the calls in your example snippet, which in some cases is unfortunate and not as "precise" as it could be. – aioobe May 15 '12 at 09:24
  • @aioobe The premise of my comment was that you DON'T ever say foo(new Object()), that you only say foo(String) and foo(Integer). In that case, a foo(Object) would never be called, but you would be forced to write it anyway. Surely in real life this situations like this would happen routinely. – Jay May 16 '12 at 16:45
  • As a comment brings my attention back to this old question, another issue occurs to me: If the JVM picked the function at run-time, then you couldn't necessarily know when you were writing the code which function would be called, nor could you control it. Yes, there would be times when you would like it to magically call the "right" function, but other times you would like to be able to definitively say, It will call foo(Object) here, not foo(String), regardless of the run-time type of the object. – Jay May 16 '12 at 16:48
  • 2
    @Jay, you're missing the point. You're presenting a faulty program and saying "If this compiled, there would be an error at run-time". The OP *did not ask* why the compiler doesn't accept more programs than it already does, he asks why the *already accepted* programs behave as they do. To clarify: If you add `public void foo(Object o) { ... }` to the snippet in your answer, why doesn't the semantics say that `foo(String s)` should get called on `foo(o1)`? – aioobe May 16 '12 at 18:04
  • 1
    ... in your second reply you say *"If the JVM picked the function at run-time, then you couldn't necessarily know when you were writing the code which function would be called"*. The JVM *does* pick the functions at run-time! Which function it picks depends on the run-time type of the callee. The OP asks why it doesn't also consider the run-time types of *the arguments* when the function is picked. – aioobe May 16 '12 at 18:05
  • @aioobe The point of my original answer is that if Java behaved as the OP suggests, it would be easy to write a program that would successfully compile, but which would be unable to find a called function at run time. The existing behavior insures that all function calls are valid. The only way that you could ensure that all function calls would work would be to write some functions that in practice are never called, but that theoretically might be called given the right combination of data. I'm not saying that a language with such a feature is unthinkable or that it might not have ... – Jay May 17 '12 at 17:32
  • 1
    ... advantages. I'm trying to point out the disadvantages such a language would have compared to the way that the designers of Java chose. – Jay May 17 '12 at 17:33
  • @aioobe RE "The JVM does pick the functions at run-time!" Well, that's not the same thing. In Java I cannot write, 'Object o="Hello"; String s=o.substring(1);' I can only call functions that are defined for the declared type of the object. That way the compiler can guarantee that the function exists. Okay, I suppose you could imagine a language that validated declared types of arguments to be sure there would always be SOME suitable function, but then at run-time checked actual types and looked for more specific versions. But imagine trying to implement that. Remember that a function can ... – Jay May 17 '12 at 17:50
  • 2
    ... have more than one parameter. It would have to check the combination of run-time types. What if you declared foo(Object,Object), foo(Object,String), and foo(String,Object). Then the user calls foo("X", "Y"). Which variant should the JVM call? You'd create all sorts of complex and ambiguous cases. – Jay May 17 '12 at 17:52
  • The same applies if you add the two methods `foo(Serializable)` and `foo(Number)` to it. Now, both are more specific than a `foo(Object)` so the call `foo(new Double(…))` should not call `foo(Object)` anymore but has become ambiguous by adding more methods. – Holger Sep 16 '14 at 08:46
7

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
  • 73,784
  • 33
  • 194
  • 347
  • 2
    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? – gtrak Oct 07 '10 at 16:13
  • 1
    @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 Oct 07 '10 at 16:22
  • 1
    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. – gtrak Oct 07 '10 at 16:26
  • @ThorbjørnRavnAndersen, *to allow static typing* -- The Wikipedia article on [multiple dispatch](http://en.wikipedia.org/wiki/Multiple_dispatch) includes Haskell, Nice, Cecil, Seed7, Fortress, among languages with support multiple dispatch, *all* of which have static typing, so I don't really see your point. – aioobe May 15 '12 at 09:03
  • @aioobe he asked for why it was so in Java. Not about Haskell, Nice, Cecil, Seed7, Fortress or any other you've read about on Wikipedia. – Thorbjørn Ravn Andersen May 15 '12 at 09:28
  • Right, but if I interpret your comment correctly, you say that Java does not allow multiple disptach in order to have static typing. I claim that there are several languages with static typing that *does* support multiple dispatch. – aioobe May 15 '12 at 09:32
  • To be precise you claim that Wikipedia states that there are several languages that do so. – Thorbjørn Ravn Andersen May 15 '12 at 09:32
  • Ok, you're saying that those languages does not both have multiple dispatch and static typing? Could you please elaborate why static typing and multiple dispatch are not compatible language features? – aioobe May 15 '12 at 09:34
  • 1
    I'm not saying so, I am just precising your statement, as I do not think you have personal experience with all these languages. And to answer your question - this is what Java uses to allow type checking purely at compilation time based on what information is available in the compiled files. Multiple dispatch in e.g. Haskell use pattern matching to select the most appropriate signature at runtime (if I recall correctly) and cannot immediately be compared to the Java approach. – Thorbjørn Ravn Andersen May 15 '12 at 09:38
4

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
  • 60,850
  • 18
  • 118
  • 163
  • 1
    Yes, technically speaking it's late binding, but it is in a sense early with regard to the argument types. Your whole answer basically boils down to your last sentence: *"Java doesn't to that at runtime, instead it simply calls the method that the compiler decided to call."* which completely avoids the actual question of *why* this is the case. – aioobe May 15 '12 at 08:54
3

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
  • 698,415
  • 94
  • 811
  • 1,216
  • 1
    Yes, the java bytecode doesn't support function polymorphism, the functionality is provided by the compiler. – gtrak Oct 07 '10 at 16:20
  • @gtrak That is absolutely correct. All the fuss about ***Java's Runtime Dynamic Overload Resolution*** ***`Strength and Power`*** was a myth after all! The compiler was just sitting there and laughing at us XD! –  Aug 18 '14 at 14:02
2

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

Stan Kurilin
  • 15,614
  • 21
  • 81
  • 132
  • 1
    *It's possible.* -- No, it's not possible to let runtime type of arguments decide which overloaded function to choose. – aioobe May 15 '12 at 09:26
  • @aioobe Exactly, compiler chooses everything beforehand and puts in place ready to be run. –  Aug 18 '14 at 14:01
1
void doSomething(Comparable c) {..}
void doSomething(Iterable i) {..}

class Foo implements Comparable, Iterable { ..}

doSomething(new Foo()); // which one??
Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • 1
    That example is problematic for statically bound method overloads too ... I think. – Stephen C Oct 07 '10 at 16:01
  • @Stephen C - yes, it is. But the OP wants things like this to work. Which they can't. – Bozho Oct 07 '10 at 16:02
  • @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 Oct 07 '10 at 16:40
1

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.

Community
  • 1
  • 1
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • 2
    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? – gtrak Oct 07 '10 at 16:00
  • 2
    Gary is right. I know about late/dynamic and early/static binding... well, I dont know everything about it obviously ;-) – Korgen Oct 07 '10 at 16:22
1

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
  • 18,669
  • 6
  • 52
  • 73