Win a copy of Programmer's Guide to Java SE 8 Oracle Certified Associate (OCA) this week in the OCAJP forum!
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic

Why generic wildcard could bypass the type safety?

 
L Yan
Greenhorn
Posts: 18
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello,

I understand why this case doesn't work. And indeed it's a compile error of "Cannot cast from List<Integer> to List<Number>".



However, I don't understand why changing the childList to use wildcard could work. I am more confused by the childList could even add and print a non-integer. Does it violate the type safety?


Thank you.
 
Campbell Ritchie
Sheriff
Pie
Posts: 49756
69
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Sounds like the thing about Cage<Animal> Cage<Lion> and Cage<Butterfly> in the Java Tutorials: Here: look for "wildcards".
 
Henry Wong
author
Marshal
Pie
Posts: 21390
84
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Why generic wildcard could bypass the type safety?


The assigment to a generic wildcard does not bypass the type safety. It is the next line, where you cast the list to something else, that broke the type check.

Henry
 
L Yan
Greenhorn
Posts: 18
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I understand why we could not add an element to List<? extends Number>. It is illegal to do:



That's why I do (List<Number>)childList. What I don't understand why it doesn't throw ClassCastException when parentList.add(3.14). Because the underlying list is a ArrayList<Integer> and 3.14 is not a integer, when Java tries to implicitly convert 3.14 to integer, is it a ClassCastException?



Act
 
Rob Spoor
Sheriff
Pie
Posts: 20606
60
Chrome Eclipse IDE Java Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Because both references regard the contents as Number, and 3.14 is a Double, and therefore also a Number. It would fail if you would cast the list back to a List<Integer>.
 
Henry Wong
author
Marshal
Pie
Posts: 21390
84
C++ Chrome Eclipse IDE Firefox Browser Java jQuery Linux VI Editor Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
I understand why we could not add an element to List<? extends Number>. It is illegal to do:


So you understand that adding is "illegal"? But why is it "illegal"? Because it can violate the type safety !!


That's why I do (List<Number>)childList. What I don't understand why it doesn't throw ClassCastException when parentList.add(3.14). Because the underlying list is a ArrayList<Integer> and 3.14 is not a integer, when Java tries to implicitly convert 3.14 to integer, is it a ClassCastException?


Generics is only at compile time. ClassCastExceptions happen at runtime, when the generic type has already been erased.


IOWs, if you use generics, try your best to *not* cast anything... you need to let the generic do it's work.

Henry
 
L Yan
Greenhorn
Posts: 18
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Henry Wong wrote:
IOWs, if you use generics, try your best to *not* cast anything... you need to let the generic do it's work.
Henry


I definitely agree with you. I don't really want to do cast List<? extends Number> to List<Number>. But it doesn't seem that I have other choice. I have this set of API:



SomeFactory class is responsible to create a instance of BasePOJO's subclass and populate the data using the template. The ABC class is responsible to return an appropriate instance of SomeFactory according to the pojo Class passed in. And XYZ class actually uses the SomeFactory to create an instance of BasePOJO's subclass. For example, BasePOJO could be Animal. Then we could have SomeFactory<Cat> and SomeFactory<Dog>. If we do XYZ.generate(Cat cat), it would retrieve SomeFactory<Cat> and use SomeFactory<Cat>.createByTemplate(Cat catTemplate) to get a Cat instance.

The problem is: this.getABC().getFactory(pojo.getClass()) returns a SomeFactory<? extends BasePOJO> instead of SomeFactory. So I could not call SomeFactory<? extends BasePOJO>.create(pojo). It's compile error.

"The method createByTemplate(capture#2-of ? extends BasePOJO) in the type SomeFactory<capture#2-of ? extends BasePOJO> is not applicable for the arguments (BasePOJO)"

That's why I do:


Please let me know your suggestion how to make this design better and if possible how to avoid casting generic. Thank you very much.

I am not sure whether this question is still falling into the original thread. I could start a new thread if necessary.
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
But it doesn't seem that I have other choice.


That's fine..... But you are complaining that generics can't detect the type correctly, when you are forcing the type.

And BTW, if you are getting "errors" because the type is wrong -- after you forced the type -- doesn't that tell you that generics was right in not allowing the operation?

Henry
 
L Yan
Greenhorn
Posts: 18
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Henry Wong wrote:

And BTW, if you are getting "errors" because the type is wrong -- after you forced the type -- doesn't that tell you that generics was right in not allowing the operation?

Henry


I agree with you that it's not because generic is wrong that I have to cast the a generic type to a non-generic type. I guess the question in my previous thread is how to update the design to use the generic correctly and avoid any manual casting. Thank you very much for your reply.

 
  • Post Reply
  • Bookmark Topic Watch Topic
  • New Topic