Why is it not legal to have the following two methods in the same class?
class Test{
void add(Set<Integer> ii){}
void add(Set<String> ss){}
}
I get the compilation error
Method add(Set) has the same erasure add(Set) as another method in type Test.
while I can work around it, I was wondering why javac doesn't like this.
I can see that in many cases, the logic of those two methods would be very similar and could be replaced by a single
public void add(Set<?> set){}
method, but this is not always the case.
This is extra annoying if you want to have two constructors that takes those arguments because then you can't just change the name of one of the constructors.
This rule is intended to avoid conflicts in legacy code that still uses raw types.
Here's an illustration of why this was not allowed, drawn from the JLS. Suppose, before generics were introduced to Java, I wrote some code like this:
class CollectionConverter {
List toList(Collection c) {...}
}
You extend my class, like this:
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
After the introduction of generics, I decided to update my library.
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
You aren't ready to make any updates, so you leave your Overrider class alone. In order to correctly override the toList() method, the language designers decided that a raw type was "override-equivalent" to any generified type. This means that although your method signature is no longer formally equal to my superclass' signature, your method still overrides.
Now, time passes and you decide you are ready to update your class. But you screw up a little, and instead of editing the existing, raw toList() method, you add a new method like this:
class Overrider extends CollectionConverter {
#Override
List toList(Collection c) {...}
#Override
<T> List<T> toList(Collection<T> c) {...}
}
Because of the override equivalence of raw types, both methods are in a valid form to override the toList(Collection<T>) method. But of course, the compiler needs to resolve a single method. To eliminate this ambiguity, classes are not allowed to have multiple methods that are override-equivalent—that is, multiple methods with the same parameter types after erasure.
The key is that this is a language rule designed to maintain compatibility with old code using raw types. It is not a limitation required by the erasure of type parameters; because method resolution occurs at compile-time, adding generic types to the method identifier would have been sufficient.
Java generics uses type erasure. The bit in the angle brackets (<Integer> and <String>) gets removed, so you'd end up with two methods that have an identical signature (the add(Set) you see in the error). That's not allowed because the runtime wouldn't know which to use for each case.
If Java ever gets reified generics, then you could do this, but that's probably unlikely now.
This is because Java Generics are implemented with Type Erasure.
Your methods would be translated, at compile time, to something like:
Method resolution occurs at compile time and doesn't consider type parameters. (see erickson's answer)
void add(Set ii);
void add(Set ss);
Both methods have the same signature without the type parameters, hence the error.
The problem is that Set<Integer> and Set<String> are actually treated as a Set from the JVM. Selecting a type for the Set (String or Integer in your case) is only syntactic sugar used by the compiler. The JVM can't distinguish between Set<String> and Set<Integer>.
Define a single Method without type like void add(Set ii){}
You can mention the type while calling the method based on your choice. It will work for any type of set.
It could be possible that the compiler translates Set(Integer) to Set(Object) in java byte code. If this is the case, Set(Integer) would be used only at compile phase for syntax checking.
I bumped into this when tried to write something like:
Continuable<T> callAsync(Callable<T> code) {....}
and
Continuable<Continuable<T>> callAsync(Callable<Continuable<T>> veryAsyncCode) {...}
They become for compiler the 2 definitions of
Continuable<> callAsync(Callable<> veryAsyncCode) {...}
The type erasure literally means erasing of type arguments information from generics.
This is VERY annoying, but this is a limitation that will be with Java for while.
For constructors case not much can be done, 2 new subclasses specialized with different parameters in constructor for example.
Or use initialization methods instead... (virtual constructors?) with different names...
for similar operation methods renaming would help, like
class Test{
void addIntegers(Set<Integer> ii){}
void addStrings(Set<String> ss){}
}
Or with some more descriptive names, self-documenting for oyu cases, like addNames and addIndexes or such.
In this case can use this structure:
class Test{
void add(Integer ... ii){}
void add(String ... ss){}
}
and inside methods can create target collections
void add(Integer ... values){
this.values = Arrays.asList(values);
}
Related
I have a class with a type parameter.
class MyObject<IdType> {
#Setter
#Getter
private IdType id;
}
And I thought I can add some method for conveniency so I did.
<T extends MyObject<? super IdType>> void copyIdTo(T object) {
object.setId(getId());
}
< T extends MyObject<? extends IdType>> void copyIdFrom(T object) {
object.copyIdTo(this);
}
And I just realized that I can do this.
void copyIdTo(MyObject<? super IdType> object) {
object.setId(getId());
}
void copyIdFrom(MyObject<? extends IdType> object) {
object.copyIdTo(this);
}
Are those two sets of methods are equivalent? Which way (or style) is prefer?
In your case, the two approaches are effectively equivalent. They both restrict the argument's type to MyObject<...> or a subtype.
Since your example methods return void there's no real benefit from making the method generic. The only important thing for your method is that the argument is a MyObject<...>—beyond that the real type is meaningless. Adding the ability to make the argument's type more specific adds nothing for the method's implementation and does nothing for the caller. In other words, it's irrelevant "fluff".
So for your examples, I would say prefer the non-generic option. It's cleaner and more straightforward.
However, if your methods returned the given argument back to the caller then making the method generic could prove useful; it would allow you to declare the return type as T. This opens up possibilities to the caller such as method chaining or invoking the method "inside" another method call, all based on the specific type passed as an argument. An example of this in the core library would be Objects.requireNonNull(T).
Another good case for making the method generic is mentioned by #Thilo in the comments:
Another case would be if your method takes multiple arguments. Then you can introduce a T to make sure those two arguments have the same type (instead of two distinct types that happen to [fulfill] the constraints individually).
Yes they are equivalent. Both sets of methods declare the same thing - that the method parameter must be of type MyObject<> or a compatible subtype (subclass).
The only reason to declare T in this way is if you need to refer to T elsewhere, such as the return type of the method, or if you have multiple parameters of the same type, or inside the method body.
I would always prefer the shorter, simpler, clearer version with less angle brackets to hurt the eyeballs :)
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.
Referring to : Wildcard Capture Helper Methods
It says to create a helper method to capture the wild card.
public void foo(List<?> i) {
fooHelper(i);
}
private <T> void fooHelper(List<T> l) {
l.set(0, l.get(0));
}
Just using this function below alone doesn't produce any compilation errors, and seems to work the same way. What I don't understand is: why wouldn't you just use this and avoid using a helper?
public <T> void foo(List<T> l) {
l.set(0, l.get(0));
}
I thought that this question would really boil down to: what's the difference between wildcard and generics? So, I went to this: difference between wildcard and generics.
It says to use type parameters:
1) If you want to enforce some relationship on the different types of method arguments, you can't do that with wildcards, you have to use type parameters.
But, isn't that exactly what the wildcard with helper function is actually doing? Is it not enforcing a relationship on different types of method arguments with its setting and getting of unknown values?
My question is: If you have to define something that requires a relationship on different types of method args, then why use wildcards in the first place and then use a helper function for it?
It seems like a hacky way to incorporate wildcards.
In this particular case it's because the List.set(int, E) method requires the type to be the same as the type in the list.
If you don't have the helper method, the compiler doesn't know if ? is the same for List<?> and the return from get(int) so you get a compiler error:
The method set(int, capture#1-of ?) in the type List<capture#1-of ?> is not applicable for the arguments (int, capture#2-of ?)
With the helper method, you are telling the compiler, the type is the same, I just don't know what the type is.
So why have the non-helper method?
Generics weren't introduced until Java 5 so there is a lot of code out there that predates generics. A pre-Java 5 List is now a List<?> so if you were trying to compile old code in a generic aware compiler, you would have to add these helper methods if you couldn't change the method signatures.
I agree: Delete the helper method and type the public API. There's no reason not to, and every reason to.
Just to summarise the need for the helper with the wildcard version: Although it's obvious to us as humans, the compiler doesn't know that the unknown type returned from l.get(0) is the same unknown type of the list itself. ie it doesn't factor in that the parameter of the set() call comes from the same list object as the target, so it must be a safe operation. It only notices that the type returned from get() is unknown and the type of the target list is unknown, and two unknowns are not guaranteed to be the same type.
You are correct that we don't have to use the wildcard version.
It comes down to which API looks/feels "better", which is subjective
void foo(List<?> i)
<T> void foo(List<T> i)
I'll say the 1st version is better.
If there are bounds
void foo(List<? extends Number> i)
<T extends Number> void foo(List<T> i)
The 1st version looks even more compact; the type information are all in one place.
At this point of time, the wildcard version is the idiomatic way, and it's more familiar to programmers.
There are a lot of wildcards in JDK method definitions, particularly after java8's introduction of lambda/Stream. They are very ugly, admittedly, because we don't have variance types. But think how much uglier it'll be if we expand all wildcards to type vars.
The Java 14 Language Specification, Section 5.1.10 (PDF) devotes some paragraphs to why one would prefer providing the wildcard method publicly, while using the generic method privately. Specifically, they say (of the public generic method):
This is undesirable, as it exposes implementation information to the caller.
What do they mean by this? What exactly is getting exposed in one and not the other?
Did you know you can pass type parameters directly to a method? If you have a static method <T> Foo<T> create() on a Foo class -- yes, this has been most useful to me for static factory methods -- then you can invoke it as Foo.<String>create(). You normally don't need -- or want -- to do this, since Java can sometimes infer those types from any provided arguments. But the fact remains that you can provide those types explicitly.
So the generic <T> void foo(List<T> i) really takes two parameters at the language level: the element type of the list, and the list itself. We've modified the method contract just to save ourselves some time on the implementation side!
It's easy to think that <?> is just shorthand for the more explicit generic syntax, but I think Java's notation actually obscures what's really going on here. Let's translate into the language of type theory for a moment:
/* Java *//* Type theory */
List<?> ~~ ∃T. List<T>
void foo(List<?> l) ~~ (∃T. List<T>) -> ()
<T> void foo(List<T> l) ~~ ∀T.(List<T> -> ()
A type like List<?> is called an existential type. The ? means that there is some type that goes there, but we don't know what it is. On the type theory side, ∃T. means "there exists some T", which is essentially what I said in the previous sentence -- we've just given that type a name, even though we still don't know what it is.
In type theory, functions have type A -> B, where A is the input type and B is the return type. (We write void as () for silly reasons.) Notice that on the second line, our input type is the same existential list we've been discussing.
Something strange happens on the third line! On the Java side, it looks like we've simply named the wildcard (which isn't a bad intuition for it). On the type theory side we've said something _superficially very similar to the previous line: for any type of the caller's choice, we will accept a list of that type. (∀T. is, indeed, read as "for all T".) But the scope of T is now totally different -- the brackets have moved to include the output type! That's critical: we couldn't write something like <T> List<T> reverse(List<T> l) without that wider scope.
But if we don't need that wider scope to describe the function's contract, then reducing the scope of our variables (yes, even type-level variables) makes it easier to reason about those variables. The existential form of the method makes it abundantly clear to the caller that the relevance of the list's element type extends no further than the list itself.
I'm reading about type inference for generics, and this code was provided as an example that fails to compile.
import java.io.*;
class LastError<T> {
private T lastError;
void setError(T t){
lastError = t;
System.out.println("LastError: setError");
}
}
class StrLastError<S extends CharSequence> extends LastError<String>{
public StrLastError(S s) {
}
void setError(S s){
System.out.println("StrLastError: setError");
}
}
class Test {
public static void main(String []args) {
StrLastError<String> err = new StrLastError<String>("Error");
err.setError("Last error");
}
}
And the explanation given in the book was:
"(It looks like the setError() method in StrLastError is overriding setError() in the LastError class. However, it is not the case. At the time of compilation, the knowledge of type S is not available. Therefore, the compiler records the signatures of these two methods as setError(String) in superclass and setError(S_extends_CharSequence) in subclass—treating them as overloaded methods (not overridden). In this case, when the call to setError() is found, the compiler finds both the overloaded methods matching, resulting in the ambiguous method call error."
I really don't understand why type S can't be inferred at compile time.
String is passed when invoking the constructor of class StrLastError,
and from the API docs, String does implement interface CharSequence,
so doesn't that mean that S for <S extends CharSequence> actually is of type String?
I've read the Java online tutorial on the topic of generics several times. I've checked "Type Inference", and Inheritance, I just don't know how the whole thing works. I really need an explanation on this question.
The points I'm stuck at are:
If the subtype can't decide S, how come the super type can decide T, because the superclass does not have an upper bound? Or does it infer that T is String because the subtype calls the supertype's constructor first?
I understand that if the Constructor is invoked as:
StrLastError<CharSequence> err = newStrLastError<>((CharSequence)"Error");
there will be no ambiguity, since it's plain method overriding then. (Or am I even wrong here?)
However, like I said in the beginning, if String is passed, why can't S be inferred to be String?
You have to remind yourself that the classes are compiled one by one. Java generics are not templates as in other languages. There will only be one compiled class and not one class per type it is used with.
This way you can see that the class StrLastError will need to be compiled in a way such that it can also be used with other classes implementing CharSequence as generic type S.
Thats why the compiler gets you two different methods instead of an overridden one. Now it would be a runtime-job to see that the subclass may have wanted to override the method in the parent just in those cases where the types suggest it. Since this behaviour is hard to understand for the developer and would possibly lead to programming mistakes, it raises an exception.
If you use CharSequence as the generic type parameter of the class StrLastError, you will call the setError method in the parent class, since the type of "Last Error" is String, which is more specific than CharSequence and Java always chooses the most specific method in case it is overloaded. (I hope it is clear the the method was not overridden in this case either)
If the subtype can't decide S, how come the super type can decide T, because the superclass does not have an upper bound? Or does it infer that T is String because the subtype calls the supertype's constructor first?
The super type isn't deciding or inferring what T is; you're explicitly telling it what T is by this declaration:
class StrLastError<S extends CharSequence> extends LastError<String>
T is now bound to String for LastError, which makes every reference to T in the parent class a concrete String.
Your child class now has a bound S extends CharSequence attached to it, but this is independent to the bounds applied to the parent class.
What happens now is that Java will compile your child class, and the result of your child class is that two methods with a signature that matches String will be created. (Key note here: A String is-a CharSequence.)
In your child class, setError(Ljava/lang/CharSequence;)V is generated as the signature for setError. Because of the way generics work, LastError#setError will be treated as if it has a signature of setError(Ljava/lang/String;)V. This is also why when you go to actually override the method, it will place a String type as your parameter instead of anything else.
So, what we arrive at are two methods that have override-equivalent signatures.
void setError(CharSequence s)
void setError(String s)
JLS 8.4.8.4. applies here.
It is possible for a class to inherit multiple methods with
override-equivalent signatures (§8.4.2).
It is a compile-time error if a class C inherits a concrete method
whose signature is a subsignature of another concrete method inherited
by C. This can happen if a superclass is generic, and it has two
methods that were distinct in the generic declaration, but have the
same signature in the particular invocation used.
I understand that if the Constructor is invoked as StrLastError err = new StrLastError<>((CharSequence)"Error"); there will be no ambiguity, since its plain method overriding then.(Or I'm even wrong here)
No, now you're messing with raw types. Interestingly enough it will work, primarily because the signatures for the two methods has become:
void setError(Object s)
void setError(String s)
You want to use generics to avoid a scenario like this; you may want to invoke the super class method at some point, but in this scenario with these bindings, it's very difficult to accomplish.
This is a tricky example, to fix it you need to change the following line:
class StrLastError<S extends CharSequence> extends LastError<S>
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Method has the same erasure as another method in type
In one of my classes I wanted to define these two methods:
private void add(List<ChangeSet> changeSetList) {
for (ChangeSet changeSet : changeSetList) {
add(changeSet);
}
}
private void add(List<Change> changeList) {
for (Change change : changeList) {
add(change);
}
}
Then I get the following error:
Method add(List<Change>) has the same erasure add(List<E>) as another method in type DataRetriever
Why isn´t this allowed? What is the problem with method definitions like that? And what should I do to avoid it? I don´t want to rename one of the methods.
That's just how the type system of Java "works". The generics List<Change> and List<ChangeSet> aren't actually different types. The generic parameters are just hints for the compiler to perform certain checks and certain casts. As far as the JVM and the type system is concerned, though, both types are actually "erased" to List<Object> (or just List if you will), and the two types are really the same, with no internal differences. Therefore, you cannot actually overload on different generics parameters, since as far as overload resolution is concerned, the two types are identical.
This limitation is part of the language syntax, not the Java runtime itself. Essentially, this rule is intended to avoid conflicts in legacy code that still uses raw types.
A compiler like javac will reject this type of overloading, but if you create a class through other means (writing your own compiler, or using a byte-code engineering library like ASM) with signatures that differ only by type parameters, the javac compiler will resolve calls the correct method in your class.
Here's an illustration of why this was not allowed, drawn from the JLS. Suppose, before generics were introduced to Java, I wrote some code like this:
class CollectionConverter {
List toList(Collection c) {...}
}
You extend my class, like this:
class Overrider extends CollectionConverter{
List toList(Collection c) {...}
}
After the introduction of generics, I decided to update my library.
class CollectionConverter {
<T> List<T> toList(Collection<T> c) {...}
}
You aren't ready to make any updates, so you leave your Overrider class alone. In order to correctly override the toList() method, the language designers decided that a raw type was "override-equivalent" to any generified type. This means that although your method signature is no longer formally equal to my superclass' signature, your method still overrides.
Now, time passes and you decide you are ready to update your class. But you screw up a little, and instead of editing the existing, raw toList() method, you add a new method like this:
class Overrider extends CollectionConverter {
#Override
List toList(Collection c) {...}
#Override
<T> List<T> toList(Collection<T> c) {...}
}
Because of the override equivalence of raw types, both methods are in a valid form to override the toList(Collection<T>) method. But of course, the compiler needs to resolve a single method. To eliminate this ambiguity, classes are not allowed to have multiple methods that are override-equivalent—that is, multiple methods with the same parameter types after erasure.
The key is that this is a language rule designed to permit continued use of raw types, not a limitation arising from the erasure of type parameters.
If you eliminate legacy code (for example, by using your own, not-strictly-Java language), this type of overload functions perfectly. Because method resolution occurs at compile-time, before erasure, type reification is not required to make this work.
in addition to the answers by adarshr and Kerrek, why not just make it generic like below:
private <T> void add(List<T> changeList) {
for (T change : changeList) {
add(change);
}
}
that should work for both cases...
Because generics are only a compile time aid to you. After compilation, there will be no generics related information stored in the bytecode.
Take a look at this:
http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
After type-erasure both methods will have a signature of private void add(List), which isn't allowed.
You need to either rename the methods or pass another argument like the class of the list values.