8

See the following example:

interface I {}

class A implements I {}

class B implements I {}

class Foo{
    void f(A a) {}
    void f(B b) {}
    static public void main(String[]args ) {
        I[] elements = new I[] {new A(), new B(), new B(), new A()};
        Foo o = new Foo();
        for (I element:elements)
            o.f(element);//won't compile
    }
}

Why doesn't overloading methods support upcasting?

If overloading was implemented at run time, it would provide much more flexibility. E.g, the Visitor Pattern would be simpler. Is there any technical reason that prevents Java from doing this?

Don Li
  • 1,095
  • 2
  • 12
  • 17
  • I'm trying to imagine how the semantics would work of this approach, and my brain just goes "ew." I expect to be able to change the implementation I'm using of an interface, and have the rest of the code work exactly the same. – Louis Wasserman May 23 '12 at 04:30
  • Because statically trying to determine the run time type of an object would be akin to just simply running the program? – Perception May 23 '12 at 04:32
  • 1
    @Perception He's asking why overload dispatch couldn't be deferred to runtime instead of resolving at compile time. That sounds like it would add huge complexity to the JVM and class-loading logic. – Jim Garrison May 23 '12 at 04:35
  • 1
    @JimGarrison - yep, I'm aware of what he's asking. I'm refusing to leap over the logical barrier that would be required to view groups of overloaded functions as a virtual lookup of 'functional equivalents' that the JVM would dispatch too at runtime. Yea, lots of additional complexity. – Perception May 23 '12 at 04:40
  • Probable duplicate or at least very similar to [Overloading in Java and Multiple Dispatch](http://stackoverflow.com/q/9759141/697630) – Edwin Dalorzo May 23 '12 at 14:53

7 Answers7

6

Overload resolution involves some non-trivial rules to determine which overload is the best fit, and it'd be hard to do these efficiently at runtime. In contrast, override resolution is easier -- in the hard case you have to just look up the foo function for the object's class, and in the easy case (e.g. when there's only one implementation, or only one implementation in this code path), you can turn the virtual method into a statically-compiled, non-virtual, non-dynamically-dispatching call (if you're doing it based on the code path, you have to do a quick check to verify that the object is actually the one you expect).

As it turns out, it's a good thing Java 1.4 and lower didn't have runtime override resolution, because that would make generics much harder to retrofit. Generics play a role in override resolution, but this information wouldn't be available at runtime due to erasure.

yshavit
  • 42,327
  • 7
  • 87
  • 124
4

There is no theoretical reason why it cannot be done. The Common Lisp Object System supports this type of construction — called multiple dispatch — although it does so in a somewhat different paradigm (methods, rather than being attached to objects, are instances of generics (or generic functions), which can do virtual dispatch at run-time on the values of multiple parameters). I believe there have also been extensions to Java to enable it (Multi-Java comes to mind, although that may have been multiple inheritance rather than multiple dispatch).

There may, however, be Java language reasons why it cannot be done, besides the language designers just thinking it shouldn't be done, that I'll leave others to reason about. It does introduce complications for inheritance, though. Consider:

interface A {}
interface B {}
class C implements A {}

class Foo {
    public void invoke(A a) {}
    public void invoke(B b) {}
}

class Bar extends Foo {
    public void invoke(C c) {}
}

class Baz extends Bar {
    public void invoke(A a) {}
}

Baz obj = new Baz();
obj.invoke(new C);

Which invoke is invoked? Baz? Bar? What is super.invoke? It is possible to come up with deterministic semantics, but they will likely involve confusion and surprise in at least some cases. Given that Java aims to be a simple language, I don't think features introducing such confusion are likely to be seen as according with its goals.

Michael Ekstrand
  • 28,379
  • 9
  • 61
  • 93
3

I don't think anyone but the designers of the language could possible answer this question. I am not nearly an expert on the subject, but I will provide just my opinion.

By reading the JLS 15.12 about Method Invocation Expressions, it is pretty clear that choosing the right method to execute is an already complicated compile-time process; above all after the introduction of generics.

Now imagine moving all this to the runtime just to support the single feature of mutimethods. To me it sounds like a small feature that adds too much complexity to the language, and probably a feature with certain amount of performance implications now that all these decisions would need to be made, over and over, at runtime, and not just once, as today it is, at compile time.

To all these we could add the fact that due to type erasure it would be impossible to determine the actual type of certain generic types. It appears to me that abandoning the safety of the static type checking is not in the best interest of Java.

At any rate, there are valid alternatives to deal with the multiple dispatch problem, and perhaps these alternatives pretty much justify why it has not been implemented in the language. So, you can use the classical visitor pattern or you can use certain amount of reflection.

There is an outdated MultiJava Project that implemented mutiple dispatch support in Java and there are a couple of other projects out there using reflection to support multimethods in Java: Java Multimethods, Java Multimethods Framework. Perhaps there are even more.

You could also consider an alternative Java-based language which does support multimethods, like Clojure or Groovy.

Also, since C# is a language pretty similar to Java in its general phillosopy, it might be interesting to investigate more on how it supports multimethods and meditate on what would be the implications of offering a similar feature in Java. If you think it's a feature worth having in Java you can even submit a JEP and it may be taken into account for future releases of the Java language.

Edwin Dalorzo
  • 76,803
  • 25
  • 144
  • 205
3

Is there any technical reason that prevents Java from doing this?

Code correctness: your current example provides two implementations of I and two corresponding methods f. However nothing prevents the existence of other classes implementing I - moving the resolution to runtime would also replace compile errors to possibly hidden runtime errors.

Performance: as others have mentioned method overloading involves rather complex rules, doing so once at compile time is certainly faster than doing it for every method invocation at runtime.

Backwards compatibility: currently overloaded methods are resolved using the compile time type of passed arguments rather than their runtime type, changing the behavior to use runtime information would break a lot of existing applications.

How to work around it

Use the visitor pattern, I do not understand how someone would think that it is hard.

interface I{
      void accept(IVisitor v);
}
interface IVisitor{
      void f(A a);
      void f(B b);
}

class A implements I{
    void accept(IVisitor v){v.f(this);}
}
class B implements I{
    void accept(IVisitor v){v.f(this);}
}
class Foo implements IVisitor{
 void f(A a) {}
 void f(B b) {}
 static public void main(String[]args ) {
    I[] elements = new I[] {new A(), new B(), new B(), new A()};
    Foo o = new Foo();
    for (I element:elements)
        element.accept(o);
 }


}
josefx
  • 15,506
  • 6
  • 38
  • 63
2

Not the answer to Java. This functionality exists in C# 4 though:

using System;

public class MainClass {     
    public static void Main() {
        IAsset[] xx = { 
             new Asset(), new House(), new Asset(), new House(), new Car() 
        };     
        foreach(IAsset x in xx) {
            Foo((dynamic)x);
        }
    }


    public static void Foo(Asset a) {
        Console.WriteLine("Asset");
    }

    public static void Foo(House h) {
        Console.WriteLine("House");
    }

    public static void Foo(Car c) {
        Console.WriteLine("Car");
    }     
}

public interface IAsset { }      

public class Asset : IAsset { }

public class House : Asset { }

public class Car : Asset { }

Output:

Asset
House
Asset
House
Car

If you are using C# 3 and below, you have to use reflection, I made a post about it on my blog Multiple Dispatch in C# : http://www.ienablemuch.com/2012/04/multiple-dispatch-in-c.html

If you want to do multiple dispatch in Java, you might go the reflection route.

Here's another solution for Java: http://blog.efftinge.de/2010/03/multiple-dispatch-and-poor-mens-patter.html

Michael Buen
  • 38,643
  • 9
  • 94
  • 118
2

Guess you just have to settle with reflection:

import java.lang.reflect.*;

interface I {}    
class A implements I {}    
class B implements I {}

public class Foo {

    public void f(A a) { System.out.println("from A"); }
    public void f(B b) { System.out.println("from B"); }

    static public void main(String[]args ) throws InvocationTargetException
        , NoSuchMethodException, IllegalAccessException 
    {
        I[] elements = new I[] {new A(), new B(), new B(), new A()};
        Foo o = new Foo();
        for (I element : elements) {
            o.multiDispatch(element);    
        }
    }

    void multiDispatch(I x) throws NoSuchMethodException
        , InvocationTargetException, IllegalAccessException 
    {    
        Class cls = this.getClass();

        Class[] parameterTypes = { x.getClass() };
        Object[] arguments = { x };

        Method fMethod = cls.getMethod("f", parameterTypes);
        fMethod.invoke(this,arguments);
    }       
}

Output:

from A
from B
from B
from A
Michael Buen
  • 38,643
  • 9
  • 94
  • 118
1

Your method says it will accept A or B which are derived classes of I, they can contain more details then I

void f(A a) {}

When you try to send super class of A in your case interface I, compiler wants a confirmation that you are actually sending A as details available in A may not be available in I, also only during runtime I will actually refer to an instance of A no such information available at compile time, so you will have to explicitly tell the compiler that I is actually A or B and you do a cast to say so.

mprabhat
  • 20,107
  • 7
  • 46
  • 63
  • I can't make much sense out of this, but if the intent is to say that what the OP wants can't be done, it can. – user207421 May 28 '12 at 21:43