I am trying to understand why we have polymorphism / dynamic binding with overridden methods, but not overloaded methods. I understand that there’s indirection that allows us to index into a vtable to allow us to do this in a language like C++ or Java. I’m curious why the same isn’t the case for resolving overloaded methods — my intuition leads me to believe we could have a level of indirection that allows us to determine at runtime which overloaded method to invoke based on the runtime type.
I’m wondering if this design decision was made for performance reasons or if there’s extra complexity I’m neglecting to consider.
I have not read the minds of the language designers, so cannot really tell you. I am thinking of it in this way:
Overridden methods are different implementations of the same method at different levels in the superclass/subclass hierarchy. The subclass generally uses the same method signature (it’s allowed to return a more specific type and to declare fewer exceptions to be thrown, but it cannot completely redefine the method header, or it will no longer be an override).
Overloaded methods are really just different methods that happen to have the same name. Then the parameter types are used for distinguishing. Just like the compiler always decides on compile time which method to call, this is also the case with overloaded methods.
As an observation (possibly a minor one), with runtime resolution of overloaded methods we could no longer statically type check the return value. Imagine we have
public boolean foo(Number n);
public String foo(Integer i);
Now I would find it perfectly natural to call the former foo() like this:
boolean result = foo(myNumber);
Now if myNumber happened to be an Integer, the latter foo() would be called instead. It would return a String and I would have a type conversion error happening on runtime. I would not be amazed.
… why then we can still have runtime polymorphism and it be considered
static typing, but it wouldn't be if we did dynamic resolution of
overloaded methods.
Java has both: a static type and a runtime type. When I store an Integer into a variable declared to be a Number, then Number is the static type and Integer is the runtime type (BTW a type is not the same as a class). And you are correct, when I do myObject.foo(arg), then the runtime type of myObject decides which implementation of foo() gets called. And conceivably the runtime type of arg could have been involved in the decision too. It would get more complicated, and I am unsure about the gain.
Overloaded methods are methods that have the same name but take different parameters. You can simply see the types and order of the parameters it takes (also the type of value it returns) as part of the name.
In C++ this is actually somewhat more "exposed" - this language internally tweaks the names to match the parameters, void h(int, char) becomes something like h__Fic and void h(int) like h__Fi. These mangled names can be occasionally seen in places like error messages about them not being resolved. In Java, this is less exposed but internally comparable. This is also called the "signature".
Then you question simplifies to "if the methods have different names, why are they resolved at the compilation time and not a the run time?". The resolution between the two methods with different signatures cannot change over time, so there is simply no reason and no benefit to delay it.
Related
From the documentation of java.lang.Class::getDeclaredMethods:
Returns an array containing Method objects reflecting all the declared methods of the class or interface represented by this Class object, including public, protected, default (package) access, and private methods, but excluding inherited methods. The declared methods may include methods not in the source of the class or interface, including bridge methods and other synthetic methods added by compilers.
If this Class object represents a class or interface that has multiple declared methods with the same name and parameter types, but different return types, then the returned array has a Method object for each such method...
AFAIK java doesn't allow a class or an interface to have methods differing only by their return type(return type is not part of a function's signature), so how would this happen?
To understand why this is possible, we need to understand that there are actually two worlds in Java: the Java language world, governed by the JLS, and the Java Virtual Machine world, governed by the JVM Specification.
Those worlds do not always match 1:1. You found one example. Another example are generics. Due to type erasure (oracle.com) the types are replaced with the upper bound through the compiler, thus all type information is lost at runtime. This is the reason why we cannot call .class on a generic parameter T.
Normally as programmers, we only interact with the Java language, and the compiler takes care of transforming the code into Bytecode that is executed on the JVM. Reflection, on the other hand, allows us to interact with objects at runtime. Thus, we get a glimpse into this world. In this specific case, the JVM Specification, $4.7.9.1 (docs.oracle.com) states:
...
A method signature encodes type information about a (possibly generic) method declaration. It describes any type parameters of the method; the (possibly parameterized) types of any formal parameters; the (possibly parameterized) return type, if any; and the types of any exceptions declared in the method's throws clause.
MethodSignature:
[TypeParameters] ( {JavaTypeSignature} ) Result {ThrowsSignature}
Result:
JavaTypeSignature
VoidDescriptor
ThrowsSignature:
^ ClassTypeSignature
^ TypeVariableSignature
...
Let's say there's a class A with the lambda function named foo (for simplicity).
From my understanding, if there is a client serializing this foo (which would be serialized to "SerializedLambda") and send it to the server, the server would also have the class A with the lambda function named foo.
Now, my question is, what if the implementation of A.foo is different between the client and the server? (assuming the arg types and the return types are the same)
Does the server
deserialize "SerializedLambda" according to its own definition with no errors.
fail to deserialize.
Given how lambdas are created dynamically and "SerializedLambda" itself just contains signature, arguments and etc instead of the actual code, I suspect it's 1, but I am a newbie who needs to learn more about how this works.
Lambda expressions are compiled into synthetic methods with an unspecified name. There can be other unspecified subtleties, like the method’s argument order for captured variables. Further, when the lambda expression accesses the this instance, there are two options, compile it to an instance method or compile it to a static method receiving the instance like an ordinary parameter.
For the resulting behavior of the lambda expression, this makes no difference. But the serialized form depends on these details and hence, is very fragile. You don’t even need to change the class, recompiling it with a different compiler could change these details. In fact, even recompiling with the same compiler could change the outcome, e.g. when the compiler’s behavior depends on some hash map with iteration order randomization.
In practice, compiler vendors try to reduce such effects and produce stable results, even when not being mandated by the specification. But when you change the class, all bets are off. One aspect you can easily see in the compiled class file is that compilers add a number to the method name, depending on the order of occurrence in the source file. Inserting another lambda expression or removing one can change the numbers of all subsequent lambda expressions.
So, a changed A class can be incompatible to your serialized lambda expression, and the best thing that can happen, is an exception during deserialization due to a mismatch. Worse than that, it could select the wrong lambda expression, because it happened to have a compatible combination of name and signature.
A safer construct is method references, as they refer to the actual target method, which is not affected by other lambda expressions or method references within the same class. However, not every method reference is compiled as a direct reference on the byte code level. In certain cases, like when referring to a varargs method or calling a super method or a method on an intersection type whose declaring class does not match the erased type, the compiler may generate a stub method for the invocation, similar to a lambda expression. So you still have to be careful.
Initial Question
I have the following code:
public static void main(String[] args) throws Exception {
test(new LinkedList());
}
public static void test(Queue qeueue){
System.out.println("Queue");
}
public static void test(List list){
System.out.println("List");
}
InteliJ is not letting me run the project.
Is there any way to work around this issue?
Which of the two would the JVM use if both are equal in specificity? Is it random?
I have read related SO q/a but no one gives an answer how to compile around it.
They just list the reason, e.g. Ambiguous method call intelliJ 15
Update
An even more nasty example is:
test(null);
I have been reading the article that is quite interesting: http://javabypatel.blogspot.be/2016/05/ambiguous-method-overloading.html
A LinkedList both a List and a Deque, which in turn is a Queue.
The test method invocation is therefore ambiguous to the compiler, as there is no way to figure which overload to invoke.
Either merge the methods to accept a unique object (e.g. a Collection), or explicitly cast to either target type (or, have different method names altogether).
Also remember: it is discouraged to use raw types, you probably want to parametrize your generic collections.
With method overloading, it's the compiler who decides which method to use. To the JVM, your two methods are completely distinct, as if they had completely different names. With method overriding, that's different, then the run-time class of the "object before the dot" decides, and that's the JVM's job.
So, you have to deal with the compiler and help him to choose the correct method. The compiler's static type analysis must clearly exclude one of the two cases.
In your code, the compiler sees test(new LinkedList()); and deduces a static type of LinkedList. Knowing that that class implements both List and Queue, he can't select one method.
You don't want to cast like in test((List) new LinkedList());. Then the only variant that you might be willing to accept is to introduce a local variable of an unambiguous type, e.g.
List arg = new LinkedList();
test(arg);
Then the compiler no longer knows about the LinkedList type, but only about List, can no longer see that the run-time instance also is a Queue and selects the List method.
But effectively, it's casting as well.
And of course, you can (and maybe should) avoid the whole mess by giving different names to methods with overlapping parameter types.
Not possible without casting as you said. Since LinkedList implements both interfaces, compiler does not which one to link and thus gives an ambiguous call error. Once you cast it, compiler gets to know the intention and links a call accordingly.
Let the following be a class in my problem:
class MyClass {
String name() {
return toString();
}
}
I want to create an instance of MethodType which describes a method with a return that is "any" object. I am trying the following:
MethodType genericTypeWithObjectReturn =
MethodType.methodType(Object.class, new Class[] {});
then try to find a method using
MethodHandle mh =
lookup.findVirtual(MyClass.class, "name", genericTypeWithObjectReturn);
Using the above line, I get an exception:
Caused by: java.lang.NoSuchMethodError: MyClass.name()Ljava/lang/Object;
My observation is that the method return type should be exactly the same type; namely I should have used String.class in the above statement to define MethodType. Is this correct? Is there a way so that I can do this in the way I described and preferrably not using reflection API?
since an answer is still missing...
The MethodType has to be exact. That's a big pain point in the MethodHandles-API, but only because you control the transformation are some of the optimizations possible. The solution is to use reflection to find the method and then use MethodHandles#unreflect* to get the handle.
There's no way to use the find{Virtual,Static} methods to get method handles without specifying the exact method type, including the return type. This is a consequence of the JVM allowing overloading on return type, even though the Java programming language does not. From the documentation for Class.getMethod (emphasis mine):
To find a matching method in a class or interface C: If C declares exactly one public method with the specified name and exactly the same formal parameter types, that is the method reflected. If more than one such method is found in C, and one of these methods has a return type that is more specific than any of the others, that method is reflected; otherwise one of the methods is chosen arbitrarily.
Note that there may be more than one matching method in a class because while the Java language forbids a class to declare multiple methods with the same signature but different return types, the Java virtual machine does not. This increased flexibility in the virtual machine can be used to implement various language features. For example, covariant returns can be implemented with bridge methods; the bridge method and the method being overridden would have the same signature but different return types.
Thus JVM methods can be made inaccessible to Class.getMethod by the presence of other methods with the same signature but different (not-more-specific) return types. Given that the purpose of method handles is to support other languages on the JVM, including languages that either support overloading on return type in the source or that generate return type overloads as part of mapping to the JVM, this hole had to be fixed by requiring the exact type to be specified. (You could imagine a method pow(int, int) with overloads returning int, long, and BigInteger, selected by the source language based on the context type in foo(pow(1000, 100000));.)
Apart from that hard requirement to make all methods accessible, it's arguably better from an API design standpoint to fail fast if the desired method is not found rather than silently select an argument-compatible but different method.
If you're happy with the lookup semantics of Class.getMethod, you can use it and call MethodHandles.Lookup.unreflect on the returned Method.
yes. you should use String.class for resolving right method type.
as i know you can't implement it with method handles api, but you can use Reflection API.
For example, I could write either of these:
class example <T>
{
...
public void insert (T data)
{
...
}
}
or
class example
{
...
public void insert (Object o)
{
...
}
}
Is there a signficant difference between the 2 in terms of performance? With generics I could restrict the type of the parameter and with the second approach I guess it wouldn't be necessary to define the type of the object as it is created.
Also, with the second approach I could basically insert anything into the class, right? Whereas with generics every element in the class would be of the same type.
Anything else I'm missing?
The only reason to write the latter is if you must target an earlier JVM. Generics are implemented by type-erasure, so they have no runtime impact - only added compile time checking which will improve your code.
Of course if you need a collection which holds any old object, or a mix of several which don't have a common superclass, you need the plain Object variation (but then your class can still be generic and instantiated with new ...<Object>).
I think you pretty much nailed it. There is no performance difference. Generics are rationalized away (Type Erasure) when the code is compiled, and don't exist anymore at runtime. They just add casts when needed and do type-checking as you stated. Neal Gafter wrote a nice overview of how they work, of the current problems with Generics and how they could be solved in the next version of Java: http://gafter.blogspot.com/2006/11/reified-generics-for-java.html
There shouldn't be a performance difference.
However, Java does not offer parameter variance, so there are situations where you will be overriding pre-generics functions such as equals, compareTo, etc. where you will have to use Objects.
Some of the encounters where I had to use 'Object' instead of Generics were those of compulsion than of a choice. When working with pre-generic code or libraries built around pre-generic api, one has little choice. Dynamic proxies for example, Proxy.newProxy() returns Object type. Passing generic context (where a context can be anything) is another instance. Some of my friends argue that are as good as no-generics. As far as performance is concerned, there shouldn't be any overhead, considering type erasure.
Regarding performance, i agree with the people above.
Regarding this point of yours
"Also, with the second approach I could basically insert anything into the class, right? Whereas with generics every element in the class would be of the same type."
One more advantage of generics is there is a type check for assignment of the example instance itself.
Say for example you had an Example e1 of and another Example e2 of , type safety would be maintained and you would never be able to do e1=e2;
while with the object example, that would be possible.