Primitives are at it again, breaking rules, I learned before. Well not technically primitive but composed of them.
I learned that whenever there's no method more specific than rest, compile time error occurs as it happens here.
public static void caller(){
z5(); // Error. Neither Integer, nor String is more specific
z5(null); // Error for the same reason
}
public static void z5(Integer...integers){
System.out.println("Integer z5 called");
}
public static void z5(String...strings){
System.out.println("String z5 called");
}
Now comes primitives into the picture.
public static void caller(){
z1(null); // Error cuz [I, [J, [F all are subclass of Object.
z1(); // SURPRISINGLY works and calls the int one. WHY?
}
public static void z1(int...integers){
System.out.println("int z1 called");
}
public static void z1(long...longs){
System.out.println("long z1 called");
}
public static void z1(float...floats){
System.out.println("float z1 called");
}
Expected compile time errors occurs here.
public static void caller(){
z1(null); // Error
z1(); // Error
}
public static void z1(int...integers){
System.out.println("int z1 called");
}
public static void z1(boolean...bools){
System.out.println("bool z1 called");
}
Now my question is, int[], float[], or any array of primitives are not primitive types then Why are they treated differently than other reference types?
--UPDATE--
#john16384 You don't think I read your "Possible duplicate" Varargs in method overloading in Java
Top answer there says You cannot combine var-args, with either widening or boxing. Besides I forgot to mention, OP's code posted there, works fine on my jdk 7.
What exactly is going on which works for (int...is) & (float...fs) but not for (Integer...is) & (Float...fs) and not for (int...is) & (boolean...bool)
Quote from the JLS about varargs invocations when multiple methods are applicable:
15.12.2.5. Choosing the Most Specific Method
If more than one member method is both accessible and applicable to a
method invocation, it is necessary to choose one to provide the
descriptor for the run-time method dispatch. The Java programming
language uses the rule that the most specific method is chosen.
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 error. In cases such as an
explicitly typed lambda expression argument (§15.27.1) or a variable
arity invocation (§15.12.2.4), some flexibility is allowed to adapt
one signature to the other.
The important part here is how methods are defined to be more specific. It basically says that int... is more specific than long... because any values you could pass to the first method could also be passed to the second method.
This will also apply to the case where you pass no arguments. int... will be the most specific (it will even see byte... as more specific!).
public static void main(String[] args) {
bla();
}
private static void bla(long... x) {}
private static void bla(int... x) {}
private static void bla(short... x) {}
private static void bla(byte... x) {} // <-- calls this one
The reason you get an error when also creating an overload boolean... is that it now is ambigious which one to call, and the compiler stops before getting to the point where it has to pick the most specific method.
Related
This question already has answers here:
Why does a Java method reference with return type match the Consumer interface?
(2 answers)
Closed 4 years ago.
public class SomeClass{
public static int someFunction(int a) {
return a;
}
public static void main(String[] args) {
Consumer<Integer> c = SomeClass::someFunction;
}
}
I'm not getting why: Consumer<Integer> c = SomeClass::someFunction;
is not producing a compilation error, since the function someFunction is a method with return value, and Consumer is representing methods with no return value
From the spec:
If the body of a lambda is a statement expression (that is, an
expression that would be allowed to stand alone as a statement), it is
compatible with a void-producing function type; any result is simply
discarded.
Same is true for method references.
It's more flexible that way. Suppose it was a compiler error to not use a return value when you called a method normally - that would be incredibly annoying. You'd end up having to use fake variables you didn't care about in some cases.
public class SomeClass
{
public static int someFunction(int a) {
return a;
}
public static void main(String[] args) {
someFunction(3); // "error" - ignoring return type
int unused = someFunction(3); // "success"
}
}
If you want a the full formal definition of what is acceptable, see 15.13.2. Type of a Method Reference.
This is called special void compatibility rule. For example how many times have you actually cared about List#add return type? Even if it does return true/false.
Pretty much the same thing here, you can invoke a method, but ignore its result. If you re-write your consumer as a lambda expression, it makes more sense:
Consumer<Integer> c = x -> {
SomeClass.someFunction(x);
return;
}
If I remember correctly from the JLS there are only some types that are allowed for this.
increment/decrement operations
method invocation
assignment
instance creation
I have the following methods:
static void f(double x)
{
System.out.println("f(double)");
}
static void f(Double xObj)
{
System.out.println("f(Double)");
}
static void f(double... s)
{
System.out.println("f(double...)");
}
public static void main(String[] args)
{
double x1 = 8.5;
Double xO1 = 5.25;
f(x1);
f(xO1);
}
Output:
f(double)
f(Double)
The rule of searching an overloaded method is the following:
Search for the overloaded method without including method with auto (un)boxing and method with ellipsis.
If no method is found, search again with including method with auto (un)boxing.
If no method is found, search again with including method with ellipsis.
This rule is applicable when calling method f with primitive parameter, but when calling with auto boxing parameter this rule is not applicable.
Can anyone explain me if this rule is correct or not ? and what is the correct one ?
Thanks for advance :)
Java picks the most specific method that fits the arguments using this rule
"This rule is applicable when calling method f with primitive parameter, but when calling with auto boxing parameter this rule is not applicable."
When you remove the method f(Double), f(xO1) will call f(double) because that was the only, and the most specific method that fits the arguments.
For reference you may check the Java Language Specification
So I have the following overloaded methods:
private static void foo(short... a)
{
System.out.println("Calling var-len");
}
private static void foo(int a, int b)
{
System.out.println("Calling int-int");
}
private static void foo(int a, double b) //(3)
{
System.out.println("Calling int-double");
}
private static void main (String[] args)
{
foo((short)2, (short)5); //This one outputs "Calling int-int"
}
I know that variable arity method has the lowest priority during method resolution phases, so in this case if I call foo((short)2, (short)4); I would get "Calling int-int".
HOWEVER, if I change method (3) to foo(short a, double b), the variable arity method is picked! (Java 7).
Could anyone explain this?
According to the specification:
http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2
"However, the declaration of a variable arity method (§8.4.1) can change the method chosen for a given method method invocation expression, because a variable arity method is treated as a fixed arity method in the first phase."
Your question is a typical example for numerical promotion in java. It tries to resolve for the exact signature match (short, short), but in your case you don't have that method. Java checks for subsequent compatible data type (say int), it promotes both short to int data types and tries to find a match, it finds it.
If none of the numeric promotion works, it will resolve to variable argument method in your code.
Please read on numeric promotions on below link..
http://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html
class A {
public void printFirst(int... va) throws IOException{
System.out.print("A");
}
public static void main(String args[]) {
try {
new B().printFirst(2);
} catch (Exception ex) {
}
}
}
class B extends A {
//#Override
public void printFirst(float... va) throws IOException{
System.out.print("B");
}
}
Why, it is showing reference to call ambiguous ??
It actually compiles if you remove the varargs notation. The literal 2 should be considered an int, not a float, so I would expect that the printFirst in A would be chosen by the compiler.
It looks like this has to do with how the compiler does method invocation conversions. This SO question says it's in the spec, but the part of accepted answer that relates to this question appears to be contradictory (it says you can't combine a widening conversion (int to float) with varargs, but then later it says this is okay). A similar problem was discussed in this question and the accepted answer concludes that this case is actually unspecified (unfortunately the link to the discussion is now broken). Making matters worse, the language guide simply suggests avoiding this type of overloading.
This appears to be a bug in your compiler; I can reproduce your compile-error in one compiler (Eclipse), but not in another (javac), and I believe the latter is correct.
According to §15.12.2.5 "Choosing the Most Specific Method" of The Java Language Specification, Java SE 7 Edition, the compile-error that you're seeing should only happen if "no method is the most specific, because there are two or more methods that are maximally specific" (plus various other restrictions). But that's not the case here: in your case, B.printFirst(float...) is not maximally specific, because a method is maximally specific "if it is accessible and applicable and there is no other method that is applicable and accessible that is strictly more specific", and in your case, A.printFirst(int...) is strictly more specific, because int is a subtype of float and float is not a subtype of int.
By the way, your class B is most likely a red herring; in Eclipse, at least, you can trigger the same compile-error by simply writing:
class A
{
public static void printFirst(float... va)
{ System.out.print("float..."); }
public static void printFirst(int... va)
{ System.out.print("int..."); }
public static void main(String args[])
{ printFirst(2); }
}
This is the sample code :
public class OverloadingExample {
public void display(Object obj){
System.out.println("Inside object");
}
public void display(Double doub){
System.out.println("Inside double");
}
public static void main(String args[]){
new OverloadingExample().display(null);
}
}
Output:
Inside double
Can anyone please explain me why the overloaded method with Double parameter is called instead of that with Object ?
Yes - because Double is more specific than Object. There's a conversion from Double to Object, but not the other way round, which is what makes it more specific.
See section 15.12.2.5 of the JLS for more information. The details are pretty hard to follow, but this helps:
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.
So here, any invocation of display(Double doub) could be handled by display(Object obj) but not the other way round.