Java Polymorphism - Specific Example - java

I've got some problems with the following example (to be more precise, with one specific line). Here's the code (question follows afterwards):
public class Up
{
public void cc(Up u) {System.out.println("A");}
public void cc(Middle m) {System.out.println("B");}
}
public class Middle extends Up
{
public void cc(Up u) {System.out.println("C");}
public void cc(Down d) {System.out.println("D");}
}
public class Down extends Middle
{
public void cc(Up u) {System.out.println("E");}
public void cc(Middle m) {System.out.println("F");}
}
public class Test
{
public static void main(String... args)
{
Up uu = new Up();
Up pp = new Middle();
Down dd = new Down();
uu.cc(pp); // "A"
uu.cc(dd); // "B"
pp.cc(pp); // "C"
pp.cc(dd); // "B"
dd.cc(pp); // "E"
dd.cc(dd); // "D"
}
}
Now uu.cc(pp); and uu.cc(dd); are pretty obvious, because uu is an instance of Up and pp "looks like" an Up aswell (at compile time). The most fitting method for dd is cc(Middle m) as dd is an instance of Down which inherits from Middle.
The lines I've got the most problems with are pp.cc(dd); and dd.cc(dd).
I'm really a bit confused about which method is chosen when and how these things are determined upon compiliation or at runtime.
I'd be glad if someone could help me understand.

Basically, the method signature is chosen at compile time based on the compile-time types of the expressions involved, and the implementation is chosen at execution time, based on the actual implementation just of the target of the method.
So at compile time, pp.cc(dd) tries to find a match for Up.cc(Down). The most specific match is Up.cc(Middle), so that's what ends up in the compiled code. Now at execution time, the implementation of that will be Up.cc(Middle) because Middle doesn't override that method signature. Hence it prints "B".
Now at compile time, dd.cc(dd) tries to find a match for Down.cc(Down). There are two relevant choices here - either Middle.cc(Down) which matches the parameter exactly, or Down.cc(Middle) which matches the target type exactly. The compiler prefers Middle.cc(Down). At execution time, again that method hasn't been overridden in Down, so it prints "D".
The relevant bit of the specification for overload resolution is 15.12, and in particular 15.12.2 - determining the method signature.

In pp.cc(dd); the compiler has to choose in between methods belonging to the type of pp, Up. The best fit is cc(Middle m). You don't override this method in Middle, so runtime the Up method is called.
In dd.cc(dd) the compiler chooses in between methods belonging to Down, Middle or Up since dd is a Down. Middle's method cc(Down) exactly matches the type of dd and is chosen.
So, compile-time the choice is made based on the declared type of the variable, and the most fitting method signature. Then normal override rules apply runtime.

Related

Matching function name but different arguments

Studying "cracking the coding interview" in Java, on page 51 I came across:
void permutation(String str){
permutation(str,"");
}
void permutation(String str, String prefix){
if(str.length()==0){
System.out.println(prefix);
} else{
for(int i=0;i<str.length();i++){
String rem=str.substring(0,i)+str.substring(i+1);
permutation(rem,prefix+str.charAt(i));
}
}
}
I get that the first permutation function takes a string and calls the second permutation function which does all the work. However, isn't the second permutation a redeclaration of the first permutation function? How will Java recognize and use the first permutation function and not overwrite it?
How will java recognize and use the first permutation function?
When you call the method, Java will see what you're trying pass into it. Based on the arguments you pass, it will decide which 'version' of the method you are trying to use.
Like others have said - this is method overloading
Unlike in Python, in Java these two declarations live side-by-side -- the second doesn't replace the first. In Java, the rule is roughly that when you call a method with multiple definitions (aka an "overloaded" method), Java will look for the one that best matches the arguments you called it with and run that method. So permutation("hi") invokes the first version, and permutation("hi", "") calls the second.
The fundamental difference here is that in Python you can imagine the interpreter reading the definitions one at a time and replacing its overall definition of permutation every time it gets a new definition. In Java, you have to think of it as reading all the definitions of permutation at once, and calling the most appropriate one for any given invocation.
(A consequence of this is that Java also checks at compile-time that every overloaded version of a method is callable: for instance, if you wrote two versions of permutation that both took just a string as their argument, the compiler would give you an error and wouldn't compile your program at all. In python you'd just get the second definition.)
To explain what the semantics are, we need to take a look at how Java methods are differentiated.
In Java, a method is identified by its signature. JLS §8.4.2 specifies that
Two methods have the same signature if they have the same name and argument types.
Important to note is that the return type of a method is not part of a method's signature. Thus if one would write:
public class Foo {
void bar(String baz) {
}
String bar(String baz) {
}
}
Both methods would have the same signature. In Java, this would lead to a compilation error since it is not allowed to have two methods with the same signature in the same class.
The behaviour changes if we take inheritance into the picture:
public class Foo {
void bar(String baz);
}
public class Zoo extends Foo {
#Override
void bar(String baz);
}
In this case, class Zoo overrides method bar(...) of class Foo. Note that the annotation is not responsible for the behaviour and merely a compile-time check to ensure that there is a method void bar(String baz) in at least one parent-class.
The code presented has two method with same name, but different signatures. This is called Overloading in Java. Thus, the method are treated as not "equal". You could rename one of those method and they would not be more or less "equal".
To make things even weirder, if methods are overloaded, the signature for the method to call is made at compile-time. That means that only the static types of parameters can be considered. Let us look at the following code and figure out what the result is:
public class Test {
public static void main(final String... args) {
final String s = "foo";
final Object o = s;
print(s);
print(o);
}
private static void print(final String s) {
System.out.println("Called with String parameter");
}
private static void print(final Object o) {
System.out.println("Called with Object parameter");
}
}
Ideon demo
Now what is the static type of s? It is the type to the left, where s was declared, thus print(final String s) is called and "Called with String parameter" is printed. What is the static type of o? Again, it is the type to the left, where o was declard, and thus print(final Object o) is called and "Called with Object parameter" is printed out. One could argue that in this trivial example, the compiler could figure out that the type of o can only be String, but basing this behaviour on the ability of the compiler to recognize types at compile-time makes it only more confusing.
In java, the whole class is loaded before a method is executed.
This means that the second method is loaded/ready before the first method is executed and the first method is loaded/ready before the second method is executed.
This allows to call a method recursively, and to call a method that will be declared later.
Also, the method is overloaded.
In java, it's possible to create multiple methods with the same name in the same class if the parameters are different. The methods will be treated as different, deoending of the argument that are passed to the method.
In other words, the name alone does not define which method is called but the signature, including the parameters(not the return value)

Implementing onChanged of MapChangeListener - why this lambda

In my JavaFX, I attempted to have an ObservableMap<String, String> and a MapChangeListener that listens to keys and values changes(adding/removing a key or the corresponding value) and then does its job.
To make the listener be effective, the method to implement is:
void onChanged(MapChangeListener.Change<? extends K,? extends V> change)
What I first did, with a lambda expression, that doesn't generate any error:
map.addListener((MapChangeListener.Change<? extends String, ? extends String> change) -> {
//code here to implement onChange method
}
And here is what I discovered, that still doesn't generate any error:
map.addListener((MapChangeListener<String, String>) change -> {
//code here to implement onChange method
}
Note the position of the round brackets in this two different examples. The second seems to me to be a cast, but I really don't understand why this second option works.
Can anyone explain me this, please?
P.S.: Actually, I came accross this because I was dealing with a
ObservableMap<String, List<String>>,
that is a multimap, and the first "way" of the two above didn't work (with the right adjustments). /EDIT: I tried again with the first "way" and actually it does work, there was an error on the code I didn't notice END EDIT/. Then I tried with the second option, and it did work, and I was dazed. Then I discovered this same "behaviour" with a simple map <String, String> and this question has arisen.
These two are equivalent. The first one, you are defining the parameter of the lambda expression - note that your bracket covers the whole change parameter. This allows the compiler to know which overload to match it against.
The second one is simply a cast. You are telling the compiler what kind of method signature to match this lambda against. (MapChangeListener<String, String>) casts the whole lambda expression into a MapChangeListener, so the compiler knows that it really is addListener(MapChangeListener). Since you have defined the single parameter defined by MapChangeListener, the compiler doesn't complain that it is wrong either.
Edit
Now that I have a bit more time, I would give you some concrete example that will help you understand a little more in depth.
public class Foo {
public final void bar(IntfA a) {}
public final void bar(IntfB b) {}
public final void bar(IntfC c) {}
}
#FunctionalInterface
public interface IntfA {
void doSomething(Double a);
}
#FunctionalInterface
public interface IntfB {
void doSomething(Integer a);
}
#FunctionalInterface
public interface IntfC {
void doSomething(Double a);
}
public class Test {
public static void main(String[] args)
{
Foo foo = new Foo();
foo.bar(a -> {}); // Ambiguous
foo.bar((Integer a) -> {}); // Okay, this is IntfB
foo.bar((Double a) -> {}); // Ambiguous between IntfA and IntfC
foo.bar((IntfC) a -> {}); // No longer ambiguous since you specified that it's IntfC
foo.bar((IntfC) (a, b) -> {}); // Method signature does not match IntfC
}
}
Edit 2
It seems like you need a little more help here.
When you define a method bar(IntfA), you are expecting an object of IntfA, regardless whether IntfA is an interface type or a class type.
Then, lambda expressions are just compile-time convenient syntax. When I write foo.bar((Integer a) -> {}), the compiler will eventually turn it into Java bytecodes (within .class file) that is equivalent to this:
foo.bar(new IntfB() {
public void doSomething(Integer a) {
}
});
That equivalence is what we call Anonymous Class.
The biggest and possibly only difference in using lambda is, it makes your code shorter. Sometimes it makes your code more readable, sometimes it makes your code less readable.
Since lambda reduces the amount of things that you need to type out, it is very easy to have a lambda expression that is ambiguous for the compiler when there are overload methods like in the example. Remember that the compiler needs to figure out which overload first, then it will help you to instantiate the object for you.
When you write foo.bar((Double a) -> {}), the compile notices that you have a lambda expression that takes in one Double parameter and returns nothing. It will then look at the three overloads of bar(). It notices that both bar(IntfA) and bar(IntfC) takes in a functional interface, and both interface's method takes in one Double parameter and returns nothing. At this point, the compiler is not sure whether it should generate bytecodes equivalent to which two set of codes:
Choice 1:
foo.bar(new IntfA() {
public void doSomething(Double a) {
}
});
Choice 2:
foo.bar(new IntfC() {
public void doSomething(Double a) {
}
});
If you write foo.bar((IntfC) a -> {}), you are already hinting to the compiler that you want it to match foo.bar(IntfC) overload. The compiler sees that you have one parameter of unknown type, but since you have already tell it to match to IntfC, it will assume that parameter is Double.
Now to the last part, calling foo.bar(IntfA) doesn't automatically call the doSomething(Double a) method specified by IntfA. In my example the bar() methods did nothing, but normally people would write something useful.
Example again:
public final void bar(IntfB obj) {
if (obj == null)
System.out.println("I was waiting for an IntfB object but I got nothing!");
else
obj.doSomething(100);
}
foo.bar((Integer a) -> {
System.out.println("I got " + a + " marks for my exam!");
});
This causes "I got 100 marks for my exam!" to be printed on the console.
Lambda in reality doesn't require its type to be expressed unless there is an ambiguity.
If you would not type change it would conflict with addListener(InvalidationListener) that has the same argument length. There are 2 ways of solving this, either by explicitly expressing the type (your first snippet) or by directing the compiler to the correct overload (second), which has nothing to do with lambda semantics.
To reiterate the second point, say you have
void print(String s)
and
void print(Integer i)
calling
print(null) would cause an ambiguity. The solution is print((String)null) which is of course not a type cast, as null has no type, but rather a compiler note.

java method overload inheritance and polymorphism

Here's a test practice question i came across, would appreciate your help in making me understand the concepts
Let Hawk be a subclass of Bird. Suppose some class has two overloaded methods void foo(Hawk
h) and void foo(Bird b). Which version would get executed in the call foo(x) after the
declaration Bird x = new Hawk();
Here's the code i have so far, could someone explain to me why foo(bird b) gets executed?
public class MPractice {
public static void main(String args[]) {
Bird x = new Hawk();
Third y = new Third();
y.foo(x);
}
}
public class Third {
void foo(Hawk h) {
System.out.println("Hawk");
}
void foo(Bird b) {
System.out.println("Bird");
}
}
When Java performs overload resolution for choosing methods, it uses that type of the variable, not the runtime type of the object, to choose the method. The type of x is Bird, so the Third method chosen is foo(Bird).
This is because polymorphism isn't involved here; we're not calling a potentially overridden method on the Bird variable x, we're just calling one of a set of overloaded methods on an unrelated class, Third.
At compile time, method invocation for overloaded methods is determined based on the type the method parameters and the compile time (or static) type of the method arguments.
In your case, Third#foo(..) can take a Hawk and it can take a Bird. In your invocation
y.foo(x);
the compile time (or static) type of the argument x is Bird, since that's how it's declared, so the Third#foo(Bird) method will be invoked.

Ambiguous Overloading with varargs [duplicate]

There seems to be a bug in the Java varargs implementation. Java can't distinguish the appropriate type when a method is overloaded with different types of vararg parameters.
It gives me an error The method ... is ambiguous for the type ...
Consider the following code:
public class Test
{
public static void main(String[] args) throws Throwable
{
doit(new int[]{1, 2}); // <- no problem
doit(new double[]{1.2, 2.2}); // <- no problem
doit(1.2f, 2.2f); // <- no problem
doit(1.2d, 2.2d); // <- no problem
doit(1, 2); // <- The method doit(double[]) is ambiguous for the type Test
}
public static void doit(double... ds)
{
System.out.println("doubles");
}
public static void doit(int... is)
{
System.out.println("ints");
}
}
the docs say: "Generally speaking, you should not overload a varargs method, or it will be difficult for programmers to figure out which overloading gets called."
however they don't mention this error, and it's not the programmers that are finding it difficult, it's the compiler.
thoughts?
EDIT - Compiler: Sun jdk 1.6.0 u18
The problem is that it is ambiguous.
doIt(1, 2);
could be a call to doIt(int ...), or doIt(double ...). In the latter case, the integer literals will be promoted to double values.
I'm pretty sure that the Java spec says that this is an ambiguous construct, and the compiler is just following the rules laid down by the spec. (I'd have to research this further to be sure.)
EDIT - the relevant part of the JLS is "15.12.2.5 Choosing the Most Specific Method", but it is making my head hurt.
I think that the reasoning would be that void doIt(int[]) is not more specific (or vice versa) than void doIt(double[]) because int[] is not a subtype of double[] (and vice versa). Since the two overloads are equally specific, the call is ambiguous.
By contrast, void doItAgain(int) is more specific than void doItAgain(double) because int is a subtype of double according the the JLS. Hence, a call to doItAgain(42) is not ambiguous.
EDIT 2 - #finnw is right, it is a bug. Consider this part of 15.12.2.5 (edited to remove non-applicable cases):
One variable arity member method named m is more specific than another variable arity member method of the same name if:
One member method has n parameters and the other has k parameters, where n ≥ k. The types of the parameters of the first member method are T1, . . . , Tn-1 , Tn[], the types of the parameters of the other method are U1, . . . , Uk-1, Uk[]. Let Si = Ui, 1<=i<=k. Then:
for all j from 1 to k-1, Tj <: Sj, and,
for all j from k to n, Tj <: Sk
Apply this to the case where n = k = 1, and we see that doIt(int[]) is more specific than doIt(double[]).
In fact, there is a bug report for this and Sun acknowledges that it is indeed a bug, though they have prioritized it as "very low". The bug is now marked as Fixed in Java 7 (b123).
There is a discussion about this over at the Sun Forums.
No real resolution there, just resignation.
Varargs (and auto-boxing, which also leads to hard-to-follow behaviour, especially in combination with varargs) have been bolted on later in Java's life, and this is one area where it shows. So it is more a bug in the spec, than in the compiler.
At least, it makes for good(?) SCJP trick questions.
Interesting. Fortunately, there are a couple different ways to avoid this problem:
You can use the wrapper types instead in the method signatures:
public static void doit(Double... ds) {
for(Double currD : ds) {
System.out.println(currD);
}
}
public static void doit(Integer... is) {
for(Integer currI : is) {
System.out.println(currI);
}
}
Or, you can use generics:
public static <T> void doit(T... ts) {
for(T currT : ts) {
System.out.println(currT);
}
}

bug with varargs and overloading?

There seems to be a bug in the Java varargs implementation. Java can't distinguish the appropriate type when a method is overloaded with different types of vararg parameters.
It gives me an error The method ... is ambiguous for the type ...
Consider the following code:
public class Test
{
public static void main(String[] args) throws Throwable
{
doit(new int[]{1, 2}); // <- no problem
doit(new double[]{1.2, 2.2}); // <- no problem
doit(1.2f, 2.2f); // <- no problem
doit(1.2d, 2.2d); // <- no problem
doit(1, 2); // <- The method doit(double[]) is ambiguous for the type Test
}
public static void doit(double... ds)
{
System.out.println("doubles");
}
public static void doit(int... is)
{
System.out.println("ints");
}
}
the docs say: "Generally speaking, you should not overload a varargs method, or it will be difficult for programmers to figure out which overloading gets called."
however they don't mention this error, and it's not the programmers that are finding it difficult, it's the compiler.
thoughts?
EDIT - Compiler: Sun jdk 1.6.0 u18
The problem is that it is ambiguous.
doIt(1, 2);
could be a call to doIt(int ...), or doIt(double ...). In the latter case, the integer literals will be promoted to double values.
I'm pretty sure that the Java spec says that this is an ambiguous construct, and the compiler is just following the rules laid down by the spec. (I'd have to research this further to be sure.)
EDIT - the relevant part of the JLS is "15.12.2.5 Choosing the Most Specific Method", but it is making my head hurt.
I think that the reasoning would be that void doIt(int[]) is not more specific (or vice versa) than void doIt(double[]) because int[] is not a subtype of double[] (and vice versa). Since the two overloads are equally specific, the call is ambiguous.
By contrast, void doItAgain(int) is more specific than void doItAgain(double) because int is a subtype of double according the the JLS. Hence, a call to doItAgain(42) is not ambiguous.
EDIT 2 - #finnw is right, it is a bug. Consider this part of 15.12.2.5 (edited to remove non-applicable cases):
One variable arity member method named m is more specific than another variable arity member method of the same name if:
One member method has n parameters and the other has k parameters, where n ≥ k. The types of the parameters of the first member method are T1, . . . , Tn-1 , Tn[], the types of the parameters of the other method are U1, . . . , Uk-1, Uk[]. Let Si = Ui, 1<=i<=k. Then:
for all j from 1 to k-1, Tj <: Sj, and,
for all j from k to n, Tj <: Sk
Apply this to the case where n = k = 1, and we see that doIt(int[]) is more specific than doIt(double[]).
In fact, there is a bug report for this and Sun acknowledges that it is indeed a bug, though they have prioritized it as "very low". The bug is now marked as Fixed in Java 7 (b123).
There is a discussion about this over at the Sun Forums.
No real resolution there, just resignation.
Varargs (and auto-boxing, which also leads to hard-to-follow behaviour, especially in combination with varargs) have been bolted on later in Java's life, and this is one area where it shows. So it is more a bug in the spec, than in the compiler.
At least, it makes for good(?) SCJP trick questions.
Interesting. Fortunately, there are a couple different ways to avoid this problem:
You can use the wrapper types instead in the method signatures:
public static void doit(Double... ds) {
for(Double currD : ds) {
System.out.println(currD);
}
}
public static void doit(Integer... is) {
for(Integer currI : is) {
System.out.println(currI);
}
}
Or, you can use generics:
public static <T> void doit(T... ts) {
for(T currT : ts) {
System.out.println(currT);
}
}

Categories