• Post Reply Bookmark Topic Watch Topic
  • New Topic

Cannot understand generics  RSS feed

 
Ruslan Salimovich
Greenhorn
Posts: 25
Java
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hello guys!
There is a Java code in the "Thinking In Java" book:



There is a comment in the code // writeExact(fruit, new Apple()); // Error:. But I have deleted double slash and I didn't get eny error. Did author made a mistake? Or why is it?
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Ruslan Salimovich wrote:There is a comment in the code "// writeExact(fruit, new Apple()); // Error:". But I have deleted double slash and I didn't get eny error.

That seems odd, unless things have changed in version 8.

Did author made a mistake? Or why is it?

1. No.
2. Because new Apple() is an object of type Apple, therefore the method will be expecting a List<Apple>, but you're passing a List<Fruit> - and the two are NOT related.

However, those are the rules as I understood them before version 8, and while I don't think anything much has changed with generics, the list does mention "improved type inference" - though exactly what that means I don't know.

HIH

Winston
 
Stephan van Hulst
Saloon Keeper
Posts: 7991
143
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Nothing has changed.

It works because when you call writeExact(), you're passing the type argument <Fruit>, which is inferred from the first argument you pass, which is a List<Fruit>. That means the method expects you to pass a Fruit as an item, and Apple is a Fruit, so it's fine.

I bet it won't work if you switch order of the method parameters, or if you call it as GenericWriting.<Apple>writeExact(fruit, new Apple());
 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stephan van Hulst wrote:I bet it won't work if you switch order of the method parameters...


I would have made that bet too, Stephan.

And we'd both have lost. The compiler has no objection to this code:


Its type-inferencing must be more powerful than we thought.
 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
By the way, Ruslan, compare Lines 10 and 11 of your code with Lines 10 and 11 of my code. Both work, but my version makes use of a recent change to Java that makes our lives slightly easier.
 
Stephan van Hulst
Saloon Keeper
Posts: 7991
143
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Yeah, I suppose it infers the most specific common supertype from the arguments passed.
 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stephan van Hulst wrote:Yeah, I suppose it infers the most specific common supertype from the arguments passed.


That's what I infer...
 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
This code
produces this complaint from the compiler:

method writeExact in class GenericWriting cannot be applied to given types;
required: T,List<T>
found: Orange,List<Apple>
reason: inference variable T has incompatible bounds
equality constraints: Apple
lower bounds: Orange
where T is a type-variable:
T extends Object declared in method <T>writeExact(T,List<T>)


Which I believe confirms your theory, Stephan.
 
Zachary Griggs
Ranch Hand
Posts: 83
10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Look at the inheritance at the top.
- An apple is a fruit
- An orange is a fruit
So, you created a list of type fruit. You can pass in anything that is a fruit to this list (fruit, apple, orange). This is an intentional feature. However, since the List contains the superclass, if you now do a get on that, and retrieve from the list, it will all come out as type fruit (not apple/orange). An orange is not an apple, so it makes sense that this one would fail.
 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Zachary Griggs wrote:Look at the inheritance at the top.
- An apple is a fruit
- An orange is a fruit
So, you created a list of type fruit. You can pass in anything that is a fruit to this list (fruit, apple, orange). This is an intentional feature. However, since the List contains the superclass, if you now do a get on that, and retrieve from the list, it will all come out as type fruit (not apple/orange). An orange is not an apple, so it makes sense that this one would fail.


Are you sure about that, Zachary? What do you think this code prints out when you run it?

 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stephan van Hulst wrote:Yeah, I suppose it infers the most specific common supertype from the arguments passed.

I presume what you're saying is that, for that call, it infers T as Fruit, not Apple - which kind of makes sense.

But is that something new for version 8, or what it's always done?

TBH, the original remark that it would be an error seemed correct to me.

Winston
 
Zachary Griggs
Ranch Hand
Posts: 83
10
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
@Steven, it will display that it is an orange because of polymorphism, but it still does not return an Orange if you put an Orange in. Consider this line:

Even though we put a MyOrange into the fruitList as position 0, this is still invalid since the get command on a Fruit list returns a Fruit, not an orange (even if polymorphism afterwards will recognize it as an orange).
 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Zachary Griggs wrote:@Steven, it will display that it is an orange because of polymorphism, but it still does not return an Orange if you put an Orange in. Consider this line:

Even though we put a MyOrange into the fruitList as position 0, this is still invalid since the get command on a Fruit list returns a Fruit, not an orange (even if polymorphism afterwards will recognize it as an orange)

No, I think it's invalid because the type of the List is Fruit, and Java is a strongly typed language. It is, in fact, returning a reference to an Orange, which is why this line compiles and runs:
 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Actually, before SvH corrects me, let me be more precise: the type of the List is List. The type of that List's get method is Fruit. But, in my example program, if get were returning a Fruit, it would print out something different than it does. It's important to know when you are talking about a type as a class, and when you are talking about the type of an instance of a class. The List's get method is of type Fruit, but in our examples, it is returning a reference to an instance of type Orange. It can do this because, as you said, an Orange is a Fruit. But to say that storing an Orange in a List<Fruit> List returns a Fruit when you call its get method is just incorrect.
 
Zachary Griggs
Ranch Hand
Posts: 83
10
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
That line also compiles if you put a fruit in at position 0, it will just fail at runtime since it's a bad cast

Another test: try removing the function identifyYourself from MyFruit, and it will no longer compile. Because the method does not return an orange, but a fruit. Overriding that method shows polymorphism, not return type.

(edit) Actually after looking in the debugger, it does return an orange reference. You were right, my bad.
 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Zachary Griggs wrote:That line also compiles if you put a fruit in at position 0, it will just fail at runtime.

That is correct, but only because it proves what I said: storing an Orange in a List<Fruit> returns an Orange from List.get, and storing a Fruit in that List returns a Fruit.

Another test: try removing the function identifyYourself from MyFruit, and it will no longer compile.

Correct.

Because the method does not return an orange, but a fruit.

I'm afraid that is incorrect. It returns the same reference that it was given: a reference to an Orange. Upcasting it to a Fruit doesn't make the reference a Fruit reference, but it does prohibit the compiler from attempting to dereference it as what it is; it is limited to dereferencing according to the type of the variable in which it is stored.

Sorry if I'm coming off a bit strongly here, but what you are saying is really misleading to a person trying to understand the OP's problem.

Try adding this code after the get and tell me what it says:


 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Zachary Griggs wrote:(edit) Actually after looking in the debugger, it does return an orange reference. You were right, my bad.


Thanks for having the class to check your own work and report back what you found. That's how we learn from each other.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!