For the code below, why do I get this compile time error:
The method overloadedMethod(IOException) is ambiguous for the type Test.
public class Test {
public static void main(String[] args) {
Test test = new Test();
test.overloadedMethod(null);
}
void overloadedMethod(IOException e) {
System.out.println("1");
}
void overloadedMethod(FileNotFoundException e) {
System.out.println("2");
}
void overloadedMethod(Exception e) {
System.out.println("3");
}
void overloadedMethod(ArithmeticException e) {
System.out.println("4");
}
}
Both FileNotFoundException and ArithmeticException are in the same level in java object hierarchy. Compiler confused to choose most specific method, Since both method are eligible for invocation.
Even Compiler can't choose most specific method, if there are multiple object hierarchies. So removing either of FileNotFoundException or ArithmeticException won't solve the problem.
From JLS 15.12.2.5
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 type error.
You have four versions of overloadedMethod that accept parameters of type IOException, FileNotFoundException, Exception and ArithmeticException.
When you call
test.overloadedMethod(null);
the JVM cannot know which version of the method you are intending to call. That is why the ambiguous error.
You need to be more specific on which version you want to call. You can do that by casting the parameter:
test.overloadedMethod((IOException)null);
Related
I know the method signature is including method name and its parameter list.
But how about throws Exception?
public List<ServiceStatusVo> listServiceStatuses() throws RetrieverException {
...
return list;
}
If it's not included then why I cannot pass in the following lambda:
() -> listServiceStatuses()
but I can pass in
() -> {
try {
return listServiceStatuses();
} catch (RetrieverException e) {
}
}
And also I can throw it out again
() -> {
try {
return listServiceStatuses();
} catch (RetrieverException e) {
throw e;
}
}
I know the Supplier<T> functional interface, that's what really confusing me if throws is not part of the method signature.
Thanks for the help.
It's not about the method signature directly. From JLS Sec 11.2.3:
It is a compile-time error if a lambda body can throw some exception class E when E is a checked exception class and E is not a subclass of some class declared in the throws clause of the function type targeted by the lambda expression.
This is a little surprising - I must admit that my initial thought was that the exception is part of the method signature.
But remember that "checked exception" means compile-time checked exception: the compiler makes sure that you have handled all checked exceptions; but once it has been compiled, checked and unchecked exception types are treated just the same. Notice that that the JVM spec doesn't even mention checkedness in the section on exceptions.
So, as seen at runtime, the method can throw any exception. And as stated in the language spec:
Two methods or constructors, M and N, have the same signature if they have the same name, the same type parameters (if any) (§8.4.4), and, after adapting the formal parameter types of N to the the type parameters of M, the same formal parameter types.
This question already has answers here:
Is it possible to have different return types for a overloaded method?
(13 answers)
The relationship of overload and method return type in Java?
(4 answers)
Closed 6 years ago.
I am gonna put this question to have a clear idea about overloading Concept in java . As per my understanding while method resolution in overloading compiler will look for method signature that is it should have same method name and different argument types . But what if the return type is different ??
class Test{
public void m1(int i) {
System.out.println(" int arg");
}
public int m1(String s) {
System.out.println("String-arg");
return (5+10);
}
public static void main (String[] args) throws java.lang.Exception
{
Test t = new Test();
t.m1(5);
int i = t.m1("ani");
System.out.println(i);
}}
the above program is running perfectly . my doubt here is , the method m1() is it overloaded ?? it has different return type . someone please make it clear. Thanks in advance
In Java methods are identified by name and arguments' classes and amount. The return type doesn't identify the method. For this reason the following code would be illegal:
public void m1(String i) {
System.out.println(" int arg");
}
public int m1(String s) {
System.out.println("String-arg");
return (5+10);
}
If two methods of a class (whether both declared in the same class, or both inherited by a class, or one declared and one inherited) have the same name but signatures that are not override-equivalent, then the method name is said to be overloaded. (...) When a method is invoked (§15.12), the number of actual arguments (and any explicit type arguments) and the compile-time types of the arguments are used, at compile time, to determine the signature of the method that will be invoked (§15.12.2). If the method that is to be invoked is an instance method, the actual method to be invoked will be determined at run time, using dynamic method lookup (§15.12.4)
Summarizing, two methods with the same name can return different types, however it's not being taken into account when deciding which method to call. JVM first decides which method to call and later checks if the return type of that method can be assigned to the certain variable.
Example (try to avoid such constructions):
public int pingPong(int i) {
return i;
}
public String pingPong(String s) {
return s;
}
public boolean pingPong(boolean b) {
return b;
}
if we follow the Oracle definition then yes, it is a overloaded method
here the info (emphasis mine)
The Java programming language supports overloading methods, and Java
can distinguish between methods with different method signatures. This
means that methods within a class can have the same name if they have
different parameter lists (there are some qualifications to this that
will be discussed in the lesson titled "Interfaces and Inheritance").
the fact that the method return a value or not is IRRELEVANT for the overloading definition...
another thing is here why can a method somethimes return a value and sometimes no...
this will drive crazy the people using the code, but that is another question...
Sorry guys, but I can not understand, on which object is synchronized block inside sync() method is synchronized:
public class TestLambda {
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(() -> {
try {
sync();
} catch (InterruptedException e) {
e.printStackTrace();
}
}).start();
}
}
static void sync() throws InterruptedException {
synchronized ((Runnable)TestLambda::new) {
System.out.println("inside");
Thread.sleep(1000L);
}
}
}
If on method reference object, why I can not just write:
synchronized (TestLambda::new) ? (it will be compile-time-error).
Any ideas?
UPD: Just in case: it is really synchronized
UPD-2: For those, who is doubting, simple example:
C:\sandbox\research\learn8\src>"C:\Program Files\Java\jdk1.8.0_31\bin\"javac TestLambda.java
TestLambda.java:27: error: method reference not expected here
public class Test { { synchronized (Test::new) { System.out.println("sync"); } } }
^
1 error
Let's see the below 2 assignments:
Supplier<TestLambda> supp = TestLambda::new;
Runnable runnable = TestLambda::new;
both of them compiles fine. Basically because a lambda or a method reference can be compatible to multiple functional interfaces. That means, merely writing TestLambda::new doesn't tell us the exact runtime type of the object created. Which interface to instantiate is determined based on target type. And that target type should always be a functional interface, which is not the case in the below statement:
synchronized(TestLambda::new) {
}
So, the compiler wouldn't allow it, as the runtime wouldn't be able to decide which functional interface to instantiate. You give that information by casting the method reference to Runnable. So, in the below statement:
synchronized((Runnable) TestLambda::new) {
}
the runtime will instantiate an object of class implementing the Runnable interface. In a sense, the casting gives a concreteness to the method reference.
To give a vague idea, this could somewhat be translated like this:
class RuntimeClass implements Runnable {
public void run() {
TestLambda testLambda = new TestLambda();
}
}
synchronized(new RuntimeClass()) {
}
P.S: Actual instance of the RuntimeClass will be singleton (as we're using a stateless method expression) -- My original incorrect statement
P.P.S: As noted from comments by #Stuart, it is not guaranteed whether for a lambda or method reference, a new instance will be created or the same will be returned. So, you shouldn't synchronize on them.
JLS 14.19. The synchronized Statement
SynchronizedStatement:
synchronized ( Expression ) Block
The type of Expression must be a reference type, or a compile-time error occurs.
JLS 15.13.2. Type of a Method Reference
A method reference expression is compatible in an assignment context, invocation context, or casting context with a target type T if T is a functional interface type (§9.8) and the expression is congruent with the function type of the ground target type derived from T.
The above two stipulations should paint the picture: the synchronized statement takes any reference-typed expression, and a method reference is compatible with any target reference type T which is a functional interface. Note that Object does not satisfy this.
In other words, there is a degree of freedom left: which exactly reference type should the method reference be compatible with? This freedom must be taken away by an explicit type cast, which forces it into that particular type. Only then has the type of the whole expression become known.
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); }
}
I've run into Java code similar to the following:
public interface BaseArg {
}
public class DerivedArg implements BaseArg {
}
public abstract class Base <A extends BaseArg> {
A arg;
void doIt() {
printArg(arg);
}
void printArg(A a) {
System.out.println("Base: " + a);
}
}
public class Derived extends Base<DerivedArg> {
void printArg(DerivedArg a) {
System.out.println("Derived: " + a);
}
public static void main(String[] args) {
Derived d = new Derived();
d.arg = new DerivedArg();
d.doIt();
}
}
(feel free to split it into files and run it).
This code ends up invoking the Derived printArg. I realize it's the only logical thing to do. However, if I perform "erasure" on the generic Base manually, replacing all occurrences of A with BaseArg, the overriding breaks down. I now get the Base's version of printIt.
Seems like "erasure" is not total - somehow printArg(A a) is not the same as printArg(BaseArg a). I can't find any basis for this in the language spec...
What am I missing in the language spec? It's not really important, but it bugs me :) .
Please note that the derived method is invoked. The question is why, considering their erased signatures are not override-equivalent.
When compiling class Derived, the compiler actually emits two methods: The method printArg(DerivedArg), and a synthetic method printArg(BaseArg), which overrides the superclass method in terms even a virtual machine ignorant of type parameters can understand, and delegates to printArg(DerivedArg). You can verify this by throwing an exception in printArt(DerivedArg), while calling it on a reference of type Base, and examining the stack trace:
Exception in thread "main" java.lang.RuntimeException
at Derived.printArg(Test.java:28)
at Derived.printArg(Test.java:1) << synthetic
at Base.doIt(Test.java:14)
at Test.main(Test.java:39)
As for finding this in the Java Language Specification, I first missed it as well, as it is not, as one might expect, specified where overriding or the subsignature relation is discussed, but in "Members and Constructors of Parameterized Types" (§4.5.2), which reveals that formal type parameters of the superclass are syntactically replaced by the actual type parameter in the subclass prior to checking for override equivalence.
That is, override equivalence is not affected by erasure, contrary to popular assumption.
If you do "manual" type erasure, you define the arg instance in BaseArg as type "BaseArg", not type "DerivedArg", so that's resolved to Base's "doIt(BaseArg)" method rather than Derived's "doIt(DerivedArg)" method. If you then alter Derived's method signature to
void printArg( BaseArg a )
from
void printArg(DerivedArg a)
it will print "Derived: arg" as expected.
I believe the behaviour that you encountered is due to the overloading method resolution.
See Java Lang Spec on overloading: link text
And also this wonderful resource on Java Generic regarding the topic.
The printArg in Derived does not override the printArg in Base. In order for it to override, by JLS 8.4.8.1, the overriding method's signature must be a "subsignature" of the overridden method's. And then by JLS 8.4.2, a subsignature must either have the same argument types (which yours doesn't), or its erasure must be the same (which is also not true).
First of all, you can compile the source code in a single file if you get rid of the "public" declarations for all of the classes/interfaces except "Derived".
Second, go ahead and do the type erasure by hand. Here's what I got when I did it:
interface BaseArg {}
class DerivedArg implements BaseArg {}
abstract class Base {
BaseArg arg;
void doIt() {
printArg(arg);
}
void printArg(BaseArg a) {
System.out.println("Base: " + a);
}
}
public class Derived extends Base {
void printArg(BaseArg a) {
System.out.println("Derived: " + a);
}
public static void main(String[] args) {
Derived d = new Derived();
d.arg = new DerivedArg();
d.doIt();
}
}
In the generic version of the code, it may look like methods Derived.printArg and Base.printArg have different signatures. However, if that were the case, then Derived.printArg could never be invoked by doIt. The type-erased version of the code makes it clear that Derived.printArg overrides Base.printArg, so doIt polymorphically calls the right method.
How is printArg in Base defined after your manual erasure ?
void printArg(BaseArg a) {
so, printArg(Derived a) does NOT override it and will not be called.
EDIT:
if you use the Override annotation in Derived, you'll get an error doing the manual erasure.