I am writing some Junit tests in Android, and if i do this:
public void testSetId(){
Friend friend = new Friend(5);
assertEquals(5,friend.getId());
}
I get an ambiguous method call error.
Ambiguous Method Call:
Both AssertEquals(int, int) and
AssertEquals(Object, Object) match
Yet If i do this:
public void testSetId(){
Integer ID = 5;
Friend friend = new Friend(ID);
assertEquals(ID, friend.getId());
}
It works. I feel like the second function should be doing the exact same thing.
What is going on here?
Before Java 5 there was no autoboxing or auto-unboxing. This meant that if a method foo had a parameter of type Integer, the following did not compile
foo(5); // 5 needs to be autoboxed
Similarly, if a method bar had a parameter of type int, this did not compile
bar(new Integer(5)); // The Integer needs to be unboxed
When autoboxing and auto-unboxing were introduced, it was essential for existing code to continue to work in exactly the same way as before. Therefore when the compiler decides which method is being called, it first considers only the applicable methods that do not require autoboxing or auto-unboxing. Only if there are no such methods does the compiler consider methods requiring autoboxing and/or auto-unboxing.
Since getId returns an Integer, the Object, Object method can be called without autoboxing in the case when the first argument is also an Integer. However the int, int method can only be called by auto-unboxing the second parameter. Therefore in your second example, the Object, Object overload is selected on the first pass.
In your first example, you are trying to pass an int and an Integer. The int, int method applies only by auto-unboxing the second argument, while the Object, Object method applies only by autoboxing the first argument. Therefore the overload cannot be chosen on the first pass, and because neither method is more specific than the other (you'll need to look that last bit up) the compiler cannot choose between the two methods.
Overload resolution is extremely complicated, and I've actually simplified it quite a bit (there are also rules involving var-args). However in practice, you don't need to learn all these rules - if ever you need to tell the compiler which method to apply you can always include an explicit cast or casts:
assertEquals((Integer) id, friend.getId());
Related
I see that the explicit cast syntax for boolean (boolean) is syntactically legal, but I can't think of a use for it. The corresponding Boolean object -- or rather casting back and forth between boolean and Boolean -- is handled by autoboxing. So it seems like a useless language artifact of the compiler. Is there a functional scenario I am missing?
It can make a difference when an overloaded method is called. Since the method to call is determined by the static type(s) of the parameter(s) (See JLS, §15.12.2), casting a Boolean to a boolean or vice-versa can change which method is called:
class Ideone {
public static void main (String[] args) {
final Boolean b = true;
foo((boolean) b); // prints out "primitive"
foo(b); // prints out "wrapper"
}
public static void foo(boolean b) {
System.out.println("primitive");
}
public static void foo(Boolean b) {
System.out.println("wrapper");
}
}
Ideone Demo
Notice that when casting from Boolean to boolean, a NullPointerException may occur when the Boolean has a value of null.
Whether this behaviour is (extensively) used or should be used is another debate, however.
rzwitserloot showed another case with boolean and Object in their answer. While rzwisterloot's case seems similar, the underlying mechanism is different since the downcast from Object to boolean is defined separately in the JLS. Furthermore, it is prone to a ClassCastException (if the Object is not a Boolean) aswell as a NullPointerException (if the Object is null).
Not entirely.
Ordinarily, applying the cast operator with a primitive type serves only one purpose, which is to convert one primitive type to another. boolean is the only primitive type with the property that nothing can be converted to a boolean, and a boolean cannot be converted to anything else.
But, there is a second purpose to these, although it's not exactly spelled out in the JLS and is a dubious code style practice: It effectively causes auto-unboxing to trigger. And it even combines the job of triggering auto-unbox and an ordinary type cast:
Object o = true;
boolean b = (boolean) o;
o = "Hello";
b = (boolean) o;
compiles just fine. It'll cause a ClassCastException on the 4th line, as expected. Without the boolean cast, you'd have to do something like:
boolean b = ((Boolean) o).booleanValue();
which is definitely more wordy. Exactly how often you end up wanting to cast some expression of type Object to boolean - probably that's a very rare occurrence, but it is a real one, and disallowing (boolean) would have made the JLS longer, not shorter (and the javac codebase wouldn't be any shorter either).
As stated in other answers, you can use a cast to boolean to enforce auto-unboxing or to select from overloaded methods. As a special case, calling invokeExact on MethodHandle may require such a cast to select the right method signature, even for the return type.
But there was a time when neither of these features existed, still, a boolean cast was allowed. Adding a special rule for boolean would not make the language simpler. But the implications of allowing this feature had been considered.
When we look into second edition of the language specification:
5.1.1 Identity Conversions
A conversion from a type to that same type is permitted for any type.
This may seem trivial, but it has two practical consequences. First, it is always permitted for an expression to have the desired type to begin with, thus allowing the simply stated rule that every expression is subject to conversion, if only a trivial identity conversion. Second, it implies that it is permitted for a program to include redundant cast operators for the sake of clarity.
The only permitted conversion that involves the type boolean is the identity conversion from boolean to boolean.
So back then, a boolean value could only be cast to boolean, that was even stated explicitly, but was intentionally allowed, e.g. “for the sake of clarity”.
Consider a call like foo(bar()) vs. foo((boolean)bar()).
Or, stringBuffer.append((boolean)someMethod()).
The cast could not alter the behavior of the program but provide additional information to the human reader.
Today’s version still states that identity conversions are allowed, but does not restrict the conversions of boolean to identity conversion, as we now can convert between boolean and reference types.
There’s even a second mentioning in the old version:
15.16 Cast Expressions
A cast expression converts, at run time, a value of one numeric type to a similar value of another numeric type; or confirms, at compile time, that the type of an expression is boolean; or checks, at run time, that a reference value refers to an object whose class is compatible with a specified reference type.
Since conversions between primitive types and reference types were not possible back then, there were three distinct use cases for casts, numeric conversions, reference type changes, or it “confirms, at compile time, that the type of an expression is boolean”.
As that’s the only thing a boolean cast could do in that Java version.
Below are the two lines of my code snippet:
List<String> listDevs = Arrays.asList("alvin", "Alchemist", "brutus", "larsen", "jason", "Kevin");
listDevs.sort(Comparator.comparing(String::length)); //This works fine
listDevs.sort(String::compareToIgnoreCase); //This works fine
But (out of expermient) when I try to write
listDevs.sort(Comparator.comparing(String::compareToIgnoreCase));
The compiler throws error
Cannot make a static reference to the non-static method
compareToIgnoreCase(String) from the type String
Similar happens to the below code
listDevs.sort(Comparator.comparing(String::compareTo));
I understand the error and that it works fine if I remove the Comparator.comparing (as shown above).
But my point is, how does this line works?
listDevs.sort(Comparator.comparing(String::length));
I believe I am missing something. I have read this thread. Is this the same scenario?
Comparator.comparing expects a Function which describes a comparable property of the elements. So String::length is sufficient as length() is a property of the String evaluating a String to an int (that’s why comparingInt is preferable here).
In contrast, String.compareToIgnoreCase and String.compareTo are comparison methods. They compare two String objects. So references to them are sufficient where a Comparator is expected, but not where a property Function is expected.
It’s like you have a factory saying “Gimme an engine, and we build a car for you” and you are trying to give them a complete car. While that existing car is valid where a car is expected, there is no sense in passing it to the factory to built a car.
Unfortunately, the current compiler implementation is very bad at reporting error with functional signatures. You will almost always see messages like “Cannot make a static reference to the non-static method …” when signatures mismatch.
The sort method expected a Comparator.
When you do this, you are indeed providing one.
listDevs.sort(Comparator.comparing(String::length));
Same happens here(but a bit non-intuitive):
listDevs.sort(String::compareToIgnoreCase)
listDevs.sort((left, right) -> left.compareToIgnoreCase(right)); // same thing as above
That's exactly the definition of a Comparator - take two Strings and return an int.
The line that you say how come this works: listDevs.sort(Comparator.comparing(String::length)); is actually pretty simple.
Comparator.comparing takes a Function that transforms your input type into something that is Comparable. In your case takes a String and returns an Integer; which is Comparable.
JLS says Compile-Time Declaration of a Method Reference of ReferenceType :: [TypeArguments] Identifier can be interpreted in different ways.
Given a targeted function type with n parameters, a set of potentially applicable methods is identified:
ReferenceType :: [TypeArguments] Identifier has two different arities, n and n-1, are considered, to account for the possibility that this form refers to either a static method or an instance method.
A method reference expression of the form ReferenceType :: [TypeArguments] Identifier can be interpreted in different ways. If Identifier refers to an instance method, then the implicit lambda expression has an extra parameter with type of this compared to if Identifier refers to a static method. It is possible for ReferenceType to have both kinds of applicable methods, so the search algorithm described above identifies them separately, since there are different parameter types for each case.
Comparator.comparing method accept a Function<T,R extends Comparable<? super R>>. when you use String::compareToIgnoreCase that will reports error,because it has two parameters one is implicit this another is a comparing string of method parameter, so it is more like a BiFunction<String,String,Integer> not a Function<String,Integer>.
BiFunction<String, String, Integer> comparator = String::compareToIgnoreCase;
// you can't assign a BiFunction to a Function
// because one is incompatiable with another.
Function<String,Integer> function = comparator;
Stream.sort method accept a Comparator, and Comparator is more like a BiFunction<T,T,Integer> so it is compatiable with String::compareToIgnoreCase. on the other hand, they can be interchangeable. for example:
Comparator<String> primary = String::compareToIgnoreCase;
BiFunction<String, String, Integer> comparator1 = primary::compare;
Comparator<String> comparator2 = comparator1::apply;
you can using comparing(String::toLowerCase) instead, it is equalivent to String::compareToIgnoreCase, for example:
// String::compareToIgnoreCase
listDevs.sort(String::compareToIgnoreCase);
// comparing(String::toLowerCase)
listDevs.sort(comparing(String::toLowerCase))
public class helloworld {
public static void main(String[] args) {
String text = "Hello World";
l(text);
int n = 0;
l("--------------------------");
l(n);
}
public static void l(Object obj) {
System.out.println(obj);
}
}
I wrote this simple program in Java and it worked. Now I am confused that if all the data types (int, char, double etc.) come under Object, then why do we specify which data type we want to accept when we pass values?
I mean we can always use the data type Object as used in the function l. Is there a specific reason why people don't always use Object as their data type to pass values?
There is an implicit conversion defined between all primitive types and their respective object counterparts:
int -> Integer
char -> Character
etc...
This is called autoboxing.
Is there a specific reason why people don't always use "Object" as their data type to pass values?
Since Java is strongly typed, you cannot do a whole lot with Object.
E.g. try this:
static Object add(Object a, Object b) {
return a + b; // won't compile
}
This is because methods, operators, etc. available to use depend on the static type of the variable.
println can accept Object because it only needs to call the toString method. If you only need the limited functionality provided by the methods in Object, then sure, you can use it as a type. This is rarely the case, however.
For the primitives you mentioned, they are not really objects, they will simply be boxed to their representation as an object. An int would become an Integer, a long would become a Long etc.
Read this article about Autoboxing in java.
As for your question
Is there a specific reason why people don't always use "Object" as
their data type to pass values?
If you specify Object as the parameter of your method you won't be able to call the methods the real object contains without doing a cast. For example, if you have a custom object AnyObject that contains a method anyMethod, you won't be able to call it without casting the object to AnyObject.
It will also be unsafe as you will be able to pass any type of object to a method which may not be designed to function properly with any of these types. A method containing only System.out.println is not representative of a real use case, it will work with any object simply because by default the println will call the toString method which is already defined in an Object.
While it does look like a function that appears to accept all types of parameters, you will have to deal with these
The function signature becomes less informative.
No more overloading
You have to do a lot of type checking and casting in the function body to avoid run time errors.
Although the method seemingly accepts all objects, you would never know the actual subset of them until you see the method definition.
The function body might end up having more code to eliminate the wrong types than for its real goal. For example, your function only prints the value. Imagine a function that predominantly does some integer operation.
Increases the probability of run time errors, as the compiler cannot throw errors for missing casts.
Why following program every time prints I'm string and not I'm object. or I'm int.?
public class Demo {
public Demo(String s){
System.out.println("I'm string");
}
public Demo(int i){
System.out.println("I'm int.");
}
public Demo(Object o){
System.out.println("I'm object.");
}
public static void main(String[] args) {
new Demo(null);
}
}
Also if I replace int with Integer. It gives error as The constructor Demo(String) is ambiguous. Why?
null can be converted to Object or String, but not int. Therefore the second constructor is out.
Between the conversion to Object or the conversion to String, the conversion to String is more specific, so that's what's picked.
The JLS section 15.12.2 describes method overload resolution, and I believe the same approach is used for constructor resolution. Section 15.12.2.5 describes choosing the most specific method (constructor in this case):
The informal intuition is that one method is more specific than another if any invocation handled by the first method could be passed on to the other one without a compile-time type error.
This about the constructor invocation with Object or String arguments - any invocation handled by new Demo(String) could also be passed on to new Demo(Object) without a compile-time type error, but the reverse is not true, therefore the new Demo(String) one is more specific... and thus chosen by the overload resolution rules.
To answer your second question (since Jon Skeet has already covered the first), when you have both a String constructor and an Integer constructor the compiler doesn't know what you mean by null in new Demo(null): it could be either a String or an Integer.
Because String can't be cast to Integer (and vice versa) the compiler gives up and reports the ambiguous error. This is in contrast to the String vs Object choice when you don't have the Integer constructor.
When you have constructors or methods like the above, the compiler generally tries to find the closest match possible and uses that.
The way to think of null in these circumstances is it acts as a subtype of all types (yes, it's not strictly speaking true but it provides a good platform for thinking about what happens). So using this rule it fits string more closely than object, hence this is the constructor that is executed.
The fact the String constructor is mentioned in by the compiler in your second error:
The constructor Demo(String) is ambiguous.
is not significant. This is because the Constructor which takes a String is the first declared constructor, so the compiler uses it in its error message. Change to have the Object constructor first and you get:
The constructor Demo(Object) is ambiguous.
What it's trying to say is there's ambiguity between the constructor which takes the Integer and the Object, so you have to be more specific, as null can be applied to each. Integer IS-NOT-A String, so the two types are not compatible. You need to be more specific so the compiler can bind the constructor call.
See #Jon Skeet answer for why the compiler raises an error in some instances and not in others.
I noticed today that auto-boxing can sometimes cause ambiguity in method overload resolution. The simplest example appears to be this:
public class Test {
static void f(Object a, boolean b) {}
static void f(Object a, Object b) {}
static void m(int a, boolean b) { f(a,b); }
}
When compiled, it causes the following error:
Test.java:5: reference to f is ambiguous, both method
f(java.lang.Object,boolean) in Test and method
f(java.lang.Object,java.lang.Object) in Test match
static void m(int a, boolean b) { f(a, b); }
^
The fix to this error is trivial: just use explicit auto-boxing:
static void m(int a, boolean b) { f((Object)a, b); }
Which correctly calls the first overload as expected.
So why did the overload resolution fail? Why didn't the compiler auto-box the first argument, and accept the second argument normally? Why did I have to request auto-boxing explicitly?
When you cast the first argument to Object yourself, the compiler will match the method without using autoboxing (JLS3 15.12.2):
The first phase (§15.12.2.2) performs
overload resolution without permitting
boxing or unboxing conversion, or the
use of variable arity method
invocation. If no applicable method is
found during this phase then
processing continues to the second
phase.
If you don't cast it explicitly, it will go to the second phase of trying to find a matching method, allowing autoboxing, and then it is indeed ambiguous, because your second argument can be matched by boolean or Object.
The second phase (§15.12.2.3) performs
overload resolution while allowing
boxing and unboxing, but still
precludes the use of variable arity
method invocation.
Why, in the second phase, doesn't the compiler choose the second method because no autoboxing of the boolean argument is necessary? Because after it has found the two matching methods, only subtype conversion is used to determine the most specific method of the two, regardless of any boxing or unboxing that took place to match them in the first place (§15.12.2.5).
Also: the compiler can't always choose the most specific method based on the number of auto(un)boxing needed. It can still result in ambiguous cases. For example, this is still ambiguous:
public class Test {
static void f(Object a, boolean b) {}
static void f(int a, Object b) {}
static void m(int a, boolean b) { f(a, b); } // ambiguous
}
Remember that the algorithm for choosing a matching method (compile-time step 2) is fixed and described in the JLS. Once in phase 2 there is no selective autoboxing or unboxing. The compiler will locate all the methods that are accessible (both methods in these cases) and applicable (again the two methods), and only then chooses the most specific one without looking at boxing/unboxing, which is ambiguous here.
The compiler did auto-box the first argument. Once that was done, it's the second argument that's ambiguous, as it could be seen as either boolean or Object.
This page explains the rules for autoboxing and selecting which method to invoke. The compiler first tries to select a method without using any autoboxing at all, because boxing and unboxing carry performance penalties. If no method can be selected without resorting to boxing, as in this case, then boxing is on the table for all arguments to that method.
When you say f(a, b), the compiler is confused as to which function it should reference to.
This is because a is an int, but the argument expected in f is an Object. So the compliler decides to convert a to an Object. Now the problem is that, if a can be converted to an object, so can be b.
This means that the function call can reference to either definitions. This makes the call ambiguous.
When you convert a to an Object manually, the compiler just looks for the closest match and then refers to it.
Why didn't the compiler select the
function that can be reached by "doing
the least possible number of
boxing/unboxing conversions"?
See the following case:
f(boolean a, Object b)
f(Object a , boolean b)
If we call like f(boolean a, boolean b), which function should it select? It ambigous right? Similarly, this will become more complex when a lot of arguments are present. So the compiler chose to give you a warning instead.
Since there is no way to know which one of the functions the programmer really intended to call, the compiler gives an error.
So why did the overload resolution
fail? Why didn't the compiler auto-box
the first argument, and accept the
second argument normally? Why did I
have to request auto-boxing
explicitly?
It didn't accept the second argument normally. Remember that "boolean" can be boxed to an Object too. You could have explicitly cast the boolean argument to Object as well and it would have worked.
See http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#20448
The cast helps because then no boxing is needed to find the method to call. Without the cast the second try is to allow boxing and then also the boolean can be boxed.
It is better to have clear and understandable specs to say what will happen than to make people guess.
The Java compiler resolves overloaded methods and constructors in phases. In the first phase [§15.12.2.2], it identifies applicable methods by subtyping [§4.10]. In this example, neither method is applicable, because int is not a subtype of Object.
In the second phase [§15.12.2.3], the compiler identifies applicable methods by method invocation conversion [§5.3], which is a combination of autoboxing and subtyping. The int argument can be converted to an Integer, which is a subtype of Object, for both overloads. The boolean argument needs no conversion for the first overload, and can be converted to Boolean, a subtype of Object, for the second. Therefore, both methods are applicable in the second phase.
Since more than one method is applicable, the compiler must determine which is most specific [§15.12.2.5]. It compares the parameter types, not the argument types, and it doesn't autobox them. Object and boolean are unrelated types, so they are considered equally specific. Neither method is more specific than the other, so the method call is ambiguous.
One way to resolve the ambiguity would be to change the boolean parameter to type Boolean, which is a subtype of Object. The first overload would always be more specific (when applicable) than the second.