// *** Key.java ***
interface Key {}
// *** Card.java ***
interface Card {}
// *** KeyCard.java ***
public class KeyCard implements Key, Card {}
// *** Door.java ***
public class Door
{
public void openWith(Key key) { System.out.println("Opened door with key"); }
public void openWith(Card card) { System.out.println("Opened door with card"); }
}
// *** Scenario.java ***
public class Scenario
{
public static void main(String[] args)
{
KeyCard keycard = new KeyCard();
Door door = new Door();
door.openWith(keycard);
}
}
What's wrong with this Java code? It won't compile:
$ javac Scenario.java
Scenario.java:15: reference to openWith is ambiguous, both method openWith(Key) in Door and method openWith(Card) in Door match
door.openWith(keycard);
^
1 error
There are two functions called openWith, which is fine, because Java supports overloaded functions. And we are passing an object to a function on the basis of which interface it implements, which is fine, because Java supports polymorphism. We have also implemented more than one interface in the
door class, which
normally is fine, because Java allows you to implement more than one interface per class, as a replacement for multiple inheritance.
The problem, though, is that in this case we have an overloaded function, openWith, capable of taking an object that implements either the key interface or the card interface, but we passed it an object that implements both the key interface and the card interface, and the compiler does not know what you want it to be passed in as. So it is an ambiguous function call.
I ran into this error in some code at work today. (Real code, not the silliness above.) I wasn't quite sure what to do. One option was to change the names of the functions in the
Door class, to something like openWithKey(Key key) and openWithCard(Card card). But that would be effectively abandoning function overloading, which is a really convenient and tidy feature in any language.
Another option would be change the
KeyCard class to support only one interface or the other. But I want KeyCard to have more than one interface implemented... that is what makes it such a cool object.
It took me a few minutes of staring at my screen to recognize the simplest, easiest, and most obvious choice. Every object variable in Java is really just a reference to the object right? So why don't we just add a few lines to the
Scenario class, like so?:
// *** Scenario.java ***
public class Scenario
{
public static void main(String[] args)
{
KeyCard keycard = new KeyCard();
Door door = new Door();
Key key = keycard;
Card card = keycard;
door.openWith(key);
door.openWith(card);
}
}
Now this compiles and runs:
$ javac Scenario.java
$ java Scenario
Opened door with key
Opened door with card
The variables
key, card, and
keycard all refer to the same object. That's fine with the compiler... it just needs to know which interface the object is supposed to look like when you pass it in.
So, in summary, make an ambiguous function call unambiguous by aliasing it before-hand to be the specific kind of interface you need at that moment.