Fu-Yang Chou wrote:
1. 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?
Fu-Yang Chou wrote:
If both T and S are "String", why is there ambiguity in the method call?
Or did something happen before class Test was compiled that became the cause of the ambiguity?
Fu-Yang Chou wrote:
One thing to ask though, to see if I'm right with type erasure.
For class StrLastError, I think the method signatures after type erasure should be setError(CharSequence s) and setError(String t) for class LastError,
which also is a kind of method overriding, and still causes the ambiguity since the compiler still can't generate the code to satisfy the situation.
Henry Wong wrote:
Fu-Yang Chou wrote:
One thing to ask though, to see if I'm right with type erasure.
For class StrLastError, I think the method signatures after type erasure should be setError(CharSequence s) and setError(String t) for class LastError,
which also is a kind of method overriding, and still causes the ambiguity since the compiler still can't generate the code to satisfy the situation.
First, the setError() method of the LastError class will Type Erase to setError(Object). Granted, the StrLastError class will confirm that it is a String, before calling the setError() method of the LastError class, but that is a compile time check. At runtime, the method parameters should take any class.
Second, as for the setError() method of the StrLastError class, I think it may be debatable. Yes, because of the bound, I believe that it does type erase to setError(CharSequence). However, when it compiles, it seems to want to type erase to setError(Object), as it is generating the ambiguity error. Which is correct? I am not sure. I will leave it up for debate.
As for method overriding for your example, meaning setError(CharSequence) overriding setError(String), that is *not* method overriding, "kind of" or otherwise -- as overriding require exact parameters. And with generics, the compiler will require exact parameters with/including the generics too.
Henry
Henry Wong wrote:
5. Generics only exist at compile time. The code that is generated uses the Object class in place of the generic (the term used to describe this is "Type Erasure"). The type checking for the generic, and what is allowed is handled at compile time only -- the run time code uses the Object class instead of the generic.
Which brings us to the ambiguity...
6. Due to point five, the setError(S s) method of the LastError class generates the code as setError(Object s), and the setError(T t) method of the StrLastError class generates the code as setError(Object t). This makes the two signature the same. And since both methods take a single Object parameter, the two methods have the same signature, and are hence, must be overriding methods.
Point four states that the two method are overloaded methods. Point five states that the two methods can only be overriding methods. The ambiguity comes from the type erasure, in that it can't generate code that can satisfy this situation. In this case, the ambiguity is not due with the compiler not being able to figure out the type of S or T, it actually doesn't need to know the type.
Henry
type parameter T is unbounded, the Java compiler replaces it with Object:
The Java compiler replaces the bounded type parameter T with the first bound class
Due to point three, the setError(S s) method of the LastError class, and the setError(T t) method of the StrLastError class are *not* overriding each other. Since S and T are different, the two methods have different signatures, and are hence, are overloaded methods.
Fu-Yang Chou wrote:This reply is wonderful
Fu-Yang Chou wrote:Does the replacement occur only when the method is invoked in line 4 of class Test?
Or has the replacement already occurred at line 3 when the constructor was invoked?
Since after erasure, class LastError replaced all Type parameters to "Object",
and class StrLastError replaced all Type parameters to its upper bound "CharSequence",
when does it actually view the parameters as of type "String"?
I hope I'm not being to obsessive, I'm just curious.
Fu-Yang Chou wrote:This works too,
which I think it is because this way StrLastError will have two methods,
one inherited from LastError<String>, which is setError(String t),
and another defined in its body, setError(CharSequence s),
and the compiler chose the more specific method to call at runtime due to polymorphism.
Am I right?
If you decompile both classes, you'll get the expected code
Why? For a combination of 3 reasons:
1/ The StrLastError class extends LastError<String> => for the compiler, the setError method in LastError has a String parameter (generic type T is replaced with String)
2/ The setError method in LastError is inherited by StrLastError
3/ The reference variable err is of type StrLastError<String> => for the compiler, the setError method in StrLastError has a String parameter (generic type S is replaced with String)
=> the compiler sees two exactly the same setError methods and thus the setError(String) method is ambiguous for the type StrLastError<String> => compiler error!
Fu-Yang Chou wrote:What confuses me the most is whether "Generic type invocation" also occurs at compile time,
and the order of that and "Type Erasure"(Maybe they're just serving two different purposes)
I just wish someone come whack my head so I can suddenly understand all this ...I feel so stupid.
Sergej Smoljanov wrote:Question about method invoke - from instance method invoke, there is no ambiguous because there is no type information about type of this (instance of class Test) because erasure?
Sergej Smoljanov wrote:I think this is because when compiled code erasure, because overloaded version of method decided at compilation time - there is no type information at that time, so for instance method of this class there is 2 method m(String s) and m(CharSequence s).