Win a copy of Modern frontends with htmx this week in the Spring forum!
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

Question about generic and ambiguous from Apress study guide (1Z0-804 and 1Z0-805)

 
Ranch Hand
Posts: 472
10
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Question about generic and ambiguous from Oracle Certified Professional Java SE 7 Programmer Exams 1Z0-804 and 1Z0-805
chapter 6 question 4


A. I t prints the following: StrLastError: setError.
B. I t prints the following: LastError: setError.
C. I t results in a compilation error.
D. I t results in a runtime exception.

Answer: C. I t results in a compilation error.
(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. Here is the error message
Test.java:22: error: reference to setError is ambiguous, both method setError(T) in LastError
and method setError(S) in StrLastError match
err.setError("Last error");
^
where T and S are type-variables:
T extends Object declared in class LastError.
S extends CharSequence declared in class StrLastError).

explain to me ambiguous when i change code this way

i have no ambiguous.
 
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I'll give you a hint/pointer and I'm quite confident you discover the reason why you don't have any ambiguity in the code.

No compiler error if the main method would use this code:

Hope it helps!
Kind regards,
Roel
 
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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, class "String" does implement interface "CharSequence",
so doesn't that make infer 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:
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?
2. I understand that if the Constructor is invoked as StrLastError<CharSequence> err = new StrLastError<>((CharSequence)"Error");
there will be no ambiguity, since its plain method overriding then.(Or I'm even wrong here)
However, like i said in the beginning, if "String" is passed, why can't S be inferred to be "String"?

I hope I am being clear enough, this problem has confused me for 2 weeks and I still can't figure out why.
Would be glad if someone can help me with the confusions.
 
author
Posts: 23950
142
jQuery Eclipse IDE Firefox Browser VI Editor C++ Chrome Java Linux Windows
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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?



First, lets clarify what is meant by "compile time". It isn't as black or white as you are thinking. The compiler cannot resolve the type T while it is compiling the LastError class. This isn't possible as the compiler doesn't know how the class will be used. And since the compiler can only resolve the type when the class is being used by another class, it is resolved at the compile time of another class.

So, yes, at the compile time of the StrLastError class, the LastError class type is being used, and the generic type of that class is resolved to be a String type. And just as with the LastError class, the generic of the StrLastError class isn't resolve at the compile time of the StrLastError class.

Henry
 
Fu-Yang Chou
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi Henry, thanks for the detailed explanation.

I understand the part that you said the concrete Type T is "String" when the subclass is compiled,
but doesn't the compiler compile the class "Test" right after?

And when "Test" is being compiled,
the compiler should realize that S is String, which implements CharSequence.

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?
 
Henry Wong
author
Posts: 23950
142
jQuery Eclipse IDE Firefox Browser VI Editor C++ Chrome Java Linux Windows
  • Likes 3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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?



Yeah. Maybe I should have been more clear with my previous post, as I was only addressing one of your points. You seem to infer that the response was to address the cause of the ambiguity. It wasn't.

Anyway ... a bunch more points, which may or may not be related. Please don't infer. I will get to the ambiguity...

1. When the compiler generates the binary for the LastError class, it must work for all types for the generic T. Remember that there is a resultant "LastError.class" file, and if you give that class file to someone else, who uses the generic differently, say as LastError<Integer>, it must still work. The compiler is not allowed to go back and generate another class file.

2. Point one is also true for the StrLastError class, and the S generic type. The only minor difference is that the generic has a bound which is handled.

3. The generic in the LastError class, and the generic in the StrLastError class, are completely independent of each other. The LastError generic can be anything. When the LastError class is used with StrLastError, T is String, but S can be anything (that satisfies the bound). Regardless, think of these two generics as separate.

4. 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.

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
 
Fu-Yang Chou
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you so much, Henry, the explanations are extremely comprehensive,
which is why I also enjoyed one of your books, Java Threads(3rd Version),
I wish I could give you 10 points :p

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.

Am I right about that?

Again, thank you very much for solving my question.
 
Henry Wong
author
Posts: 23950
142
jQuery Eclipse IDE Firefox Browser VI Editor C++ Chrome Java Linux Windows
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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
 
Fu-Yang Chou
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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



Quoted from the book "the compiler records the signatures of these two methods as setError(String) in superclass and setError(S_extends_CharSequence) in subclass";
However, from the tutorials http://docs.oracle.com/javase/tutorial/java/generics/genMethods.html , type erasure should replace the Type parameter with "Object"
I'm confused again...

If I change the declaration of StrLastErr to "class StrLastError extends LastError<S>", the compilation passes,and the setError method in the subclass is called.
In this case, T and S are still different Type parameters, and the method setError is still considered overloaded.
After type erasure, it will be setError(Object t) in class "LastError" and setError(CharSequence s) or setError(Object s) in class "StrLastError".
Thus if the setError is erased into setError(Object s) then compile error should occur, but there isn't, so I assume it compiles the method to setError(CharSequence s).
Also, if i change the statement in the first line of main to "LastError<String> err = new StrLastError<>("Error");", the compilation also passes, and calls the method in the superclass.

So I assume that there has to be something to do with the class "Test", but you said it has nothing to do with the actual type assigned,
so the question is left to the subclass "StrLastError", but I'm still pretty stuck here.

And yes, you're right about the method overriding part,
I was completely misinterpreting the terms "subsignatures" and "override-equivalent" in the JLS.
Thank you for the lessons.
 
Sergej Smoljanov
Ranch Hand
Posts: 472
10
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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


Erasure of Generic Types from The Java™ Tutorials

type parameter T is unbounded, the Java compiler replaces it with Object:


and from another example

The Java compiler replaces the bounded type parameter T with the first bound class


but in example from tutorial there is interface, and i modify for more apt example for my question:

since i can use param.compareTo(this); i think that The Java compiler replaces the bounded type parameter T with the first bound class or interface.
But i cant understand why
LastError class generates the code as setError(Object s) and
StrLastError class generates the code as setError(Object t).
Why second - StrLastError class generates the code as setError(Object t)???why not setError(CharSequence t)?
--------- question not relevant because i found answer (i think i found)----------
finally i found some post:
http://www.angelikalanger.com/GenericsFAQ/FAQSections/ProgrammingIdioms.html#FAQ051 that push me to some direction
you are right at 1-4

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.


but for compilation of invocation next happen: (whole next for compile time of class Test)
5. StrLastError<String> err = new StrLastError<String>("Error"); - at that point
this line

matter for object err -
LastError `s

become

and StrLastError<String>
mean that for object err

become
because you made object new StrLastError<String>("Error");
6. But because at the moment to check err has two method because (1-4 explanation) there is two method this lead to ambiguous.

my assumption that when compile class Test and check what can/can`t do for object of new StrLastError<String>("Error"); compiler try infer as i described above. At program run after erasure for LastError<T>, T become Object; and for StrLastError<S extends CharSequence>, S become CharSequence.
correct me if i wrong.
 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This is just a very, very tricky question

If you decompile both classes, you'll get the expected codeSo nothing wrong with this code. It's completely valid! Otherwise I wasn't able to compile (and decompile) this code The StrLastError class has 2 overloaded setError methods: one taking an Object parameter and the other one taking a CharSequence parameter.

Even creating the StrLastError instance in the main method is not a problem. Still valid code! But when you try to invoke the setError method with a String parameter you'll get a compiler errorWhy? 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!

There are a few different ways to make this code compile. Here are a few possible options (which can be applied independently to make the code compile):
1/ Mark the setError method in LastError as private
2/ Change the class declaration of StrLastError and use S or every type which IS-NOT-A String (instead of String) as the generic type parameter of LastError
3/ Use another generic type parameter for reference variable err, you can use every type which IS-A CharSequence and IS-NOT-A String (e.g. CharSequence and StringBuilder)

Hope it helps!
Kind regards,
Roel
 
Fu-Yang Chou
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This reply is wonderful,
but I still have a few questions.

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.

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?
 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Likes 2
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Fu-Yang Chou wrote:This reply is wonderful


Glad to hear you liked it!

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.


I used the "generic type X is replaced with TypeY" rule just as a mental aid for educational purposes only. This simple rule is much easier than the actual work of the compiler. Luckily you don't need to know for the exam how the compiled code will look like, because it's sometimes (much) more complex than just replacing a few type parameters. The compiler applies type erasure at compile time of course (not on line 3 or 4); when you compile your code, the compiler is activated and does all its magic More detailed information about type erasure (with some very nice code examples of code before and after compilation) can be found in Oracle's Generics tutorial.

Just for fun let's take a look at this code snippet:When you decompile the .class file of the previouse code snippet, you'll get this code:Again that's what you expected: no type parameters anymore.

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?


Yes and no!

You are correct about another alternative to make the code compile. Changing the type of reference variable err to LastError<String> will indeed result in valid code which compiles successfully:
But your explanation about why it works, is wrong! It has nothing to do with generics, it's so much easier. It's almost too easy to explain Because you changed the type of reference variable err to LastError<String>, the compiler knows you can only access/execute 1 method: the setError method in LastError. And because the LastError class has only 1 setError method, this method can never be ambiguous The compiler doesn't execute any code, so it doesn't know (and doesn't care) you assigned a StrLastError<String> instance to reference variable err, the compiler is only interested in the type of reference variable err! Based on that type, the compiler decides which methods you can execute (and which method is actually executed is determined at runtime).

And just for completeness. With this (original) class declaration of StrLastErrorthe setError method is NOT an override of the setError method defined in LastError. It's an overload => no polymorphism! Executing this coderesults in this output: LastError: setError

If you change the class declaration of StrLastError tothe setError method IS a (valid) override of the setError method defined in LastError => polymorphism! You'll notice the polymorphism, when you execute the same code, you'll get a different output: StrLastError: setError

Hope it helps!
Kind regards,
Roel
 
Fu-Yang Chou
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I read the tutorial before I made the first reply here.
Maybe I just don't get it.

I'll try to be as clear as I can to point out which part I don't understand.

This is what you replied in the post before the last reply.

If you decompile both classes, you'll get the expected code




I'm fine with this part, it is plain type erasure.(Hopefully I'm right)

But then you said

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!



Here are the major points I'm stuck at now.
The method signature should be setError(Object t) and setError(CharSequence s) for the superclass and subclass,
why does it become setError(String t) and setError(String s), which is different from what they have been compiled to,
does it have something to do with the compilation of class "Test"?

I've read https://docs.oracle.com/javase/tutorial/java/generics/types.html
So I know that replacing Type parameters with a concrete type is called Generic type invocation.

And I've also read https://docs.oracle.com/javase/tutorial/java/generics/genMethods.html
So I know type erasure for generic methods.

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.
 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Likes 3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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.


You don't have to feel stupid, generics is one of the hardest topics to understand! But it requires a little bit of explanation, so fasten your seatbelt, here we go!

First of all, forget about the decompiled code and type erasure. It's not on the exam at all and it probably adds an unnecessary complexity which confuses you. So completely forget anything we have discussed in this thread about decompiled code. Or wait... You need to remember one very easy thing about generics (and this is clearly visible in the decompiled code): you only have generics at compile time, not runtime (so no <> in the decompiled code).

When you use generics, you know one of the benefits is getting stronger type checks at compile time. So if you have code like thisyou know the compiler will do some checks: if you use the add method you can only use String objects as parameter (otherwise you'll get a compiler error). Another benefit: no cast needed anymore if you retrieve an element from the list. But that one is less important in this discussion. When you use generics, it's important to know that the compiler will perform stronger type checks at compile time. And if your code has a violation of the rules (e.g. adding an Integer to a list of String), your code will not compile! If everything is fine according to the compiler, the code compiles and you'll get byte code which can be executed, but it doesn't matter to you - the developer - how this compiled code looks like. Because the compiler already confirmed your code is valid. Little reminder: this does not mean that you won't have runtime exceptions when running this application, because the compiler doesn't check everything, so you can still access a list with an out-of-bounds index for example.
Now back to the LastError example. Let's have a look at the class definition once moreAnd let's create an instance of this classSo when you compile this code snippet, the compiler will check if everything is ok. Because you used String as generic type parameter, the compiler will perform the type checks I mentioned earlier. And for simplicity: because you used String as generic type parameter in the reference variable declaration, the compiles replaces every occurence of T with String and uses this temporary code to perform its type checks (important note: only for educational purposes, this doesn't happen at all )Now the compiler checks (using this temporary class) if every method invocation is valid. And the code snippet fails to compile, because the invocation of the setError method on line2 uses a StringBuilder parameter and StringBuilder IS-NOT-A String, so the compiler is not a happy camper! And if the compiler isn't happy, you won't get the necessary .class files to run your application.

If you understand everything until this point, this last part will be very similar. So let's add the code of the StrLastError classAnd finally the Test classIn this code the compiler needs to replace two generic type parameters:
1/ in LastError: T with String (StrLastError inherits from LastError<String>)
2/ in StrLastError: S with String (the type of the reference variable err is StrLastError and String is used as generic type parameter in the reference variable declaration).

So this is the temporary code the compiler uses to perform its type checks (important note: again only for educational purposes)And again the compiler is not happy: on line1 you invoke the setError method with a String parameter, but the compiler sees the StrLastError class has 2 setError methods with exactly the same method declaration (and both using a String parameter), so the compiler doesn't know which one to take: the one defined in StrLastError or the one defined in (and inherited from) LastError. So you'll get a compiler error, because the setError(String) is ambiguous for type StrLastError.

Pfew, almost there! Finally 2 important notes:
1/ it's very understandable if you would think that the setError method in the StrLastError class is a (valid) override of the setError method in the LastError, but it is not! Why? Because the generic type parameter in class LastError and StrLastError are different and totally unrelated: in the 1st class it's T and in the latter one it's S and there's no relation between both generic type parameters. So although both are replaced with the same type String, the compiler sees this as 2 different methods (and thus not an override). It would be an override if the different generic type parameters would be related as in this class declaration of StrLastError:So you still use two different generic type parameters (T and S), but they are actually the same generic type parameter: you used S as generic type parameter in the LastError declaration (and so all Ts will be "replaced" with S).
2/ if you would remove line1 from the Test class, the code will compile successfully. Because the compiler doesn't need to decide which setError method to invoke. And the given code is completely 100% valid, so no reason for the compiler to complain. You might use the reference variable err like thisAnd that's also not a problem for the compiler, because LastError defines only 1 setError method and it takes a String parameter => no ambiguity and the appropriate parameter => the compiler is happy to compile this code for you and deliver the very desired .class files!

That's all folks!

Hope it helps clear your doubts!
Kind regards,
Roel
 
Fu-Yang Chou
Greenhorn
Posts: 7
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you very much, my doubts are cleared.
Your reply is beyond description.

p.s. I passed OCA on 7/1, now preparing for OCP.
 
Sergej Smoljanov
Ranch Hand
Posts: 472
10
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

This question not full clear for me.
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?
 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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?


Change the first line in the main method toThen both invocations of the m method will compile successfully. Can you figure out why the instance method does compile with the original code? Or do you need some additional explanation?
 
Sergej Smoljanov
Ranch Hand
Posts: 472
10
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
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).
if i use:
it will supplied type information for new instance of class Test, and i get error as i have before. also i think that this reference has no type information (about type of itself)
 
Roel De Nijs
Sheriff
Posts: 11604
178
Hibernate jQuery Eclipse IDE Spring MySQL Database AngularJS Tomcat Server Chrome Java
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

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).


It's similar to this explanation.

Class Test has a method m with a String parameter and an overloaded version with the generic parameter T.

1/ When you create a Test instance with this codeyou'll use String as the generic type => the overloaded version of method m also has a String parameter => you'll have two methods with the same name (m) and the same parameter list (only one String parameter). So when you invokethe compiler can't know which method to invoke because both methods are exactly the same => compiler error: method m is ambiguous

2/ When you create a Test instance with this codeyou'll use CharSequence as the generic type => the overloaded version of method m has a CharSequence parameter => you'll have two methods with the same name (m) but with a different parameter list (one has a String parameter, the other one a CharSequence parameter). So when you invokethe compiler knows which method to invoke because you invoke method m with a String parameter and there's exactly one method which takes a String parameter => code compiles successfully and prints m(String s)

3/ Now let's look at the invocation of the m method (in the invoke method). You are invoking the m method on the special this reference variable. The type of this is of course Test. But what's the generic type? As you know when you have a class with a generic parameter, there is only 1 class. You don't have a class for each generic type you are using. So the generic type of the special this reference variable is the one used in the class declaration and thus it's CharSequence. And so the type of the special this reference variable is Test<CharSequence>. That's exactly the same as the reference variable t from the second scenario and that explains why the invoke method compiles successfully (and prints m(String s) as well).

4/ If you would change the class declaration tothe code won't compile because both m methods will have the same method signature m(String) and that's (obviously) a compiler error.

Hope it helps!
Kind regards,
Roel
 
There is no "i" in denial. Tiny ad:
a bit of art, as a gift, the permaculture playing cards
https://gardener-gift.com
reply
    Bookmark Topic Watch Topic
  • New Topic