• Post Reply Bookmark Topic Watch Topic
  • New Topic

Addition of Generic Type Parameter Causes Member Type Clash  RSS feed

 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Got a problem with generics, which I'm still pretty new at. Here's a program that compiles fine:



It's useless, but it compiles. If I change Line 14, however, to add a generic type parameter to the ListHolder class, Line 10 no longer compiles:



I get this error:

Uncompilable source code - incompatible types: java.lang.Object cannot be converted to javax.swing.JComponent
at experiments.Experiments.main(Experiments.java:10)


Apparently, the introduction of the type parameter leaves the compiler thinking that aList is of type Object. I can cast it, like this:


That makes the compiler happy, but why is it necessary? How does adding the (unused) type parameter to the ListHolder class end up making the compiler think the aList member of an instance of ListHolder is of type Object?
 
Winston Gutkowski
Bartender
Posts: 10575
66
Eclipse IDE Hibernate Ubuntu
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stevens Miller wrote:That makes the compiler happy, but why is it necessary? How does adding the (unused) type parameter to the ListHolder class end up making the compiler think the aList member of an instance of ListHolder is of type Object?

It's really got nothing to do with that. Your declaration assignment has to provide a type on both sides of the '=', viz:

public final ArrayList<JComponent> aList = new ArrayList<JComponent>();

or, in version 7 or later:
public final ArrayList<JComponent> aList = new ArrayList<>();

Winston
 
Stephan van Hulst
Saloon Keeper
Posts: 7969
143
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Hi Stevens,

You've stumbled across a very interesting issue. I don't know the Java spec well enough to tell you if this is a bug or not, but the problem stems from you using raw types.

In your second example, you're declaring ListHolder to be a generic type. This means that holder is of a raw type (since you didn't specify the generic type argument when you declared it). This might break all the generics that go on inside of it, which would lead the compiler to treat aList as a raw type as well.

Indeed, when you declare holder as a ListHolder<String>, it does work. What is weird though, is that you can first assign aList to a temporary variable of type ArrayList<JComponent>, and then use the iterator on it without problems. This is very inconsistent behavior.

Again, the specs may have something to say about raw types, but it could also be a bug.

Regardless, never use raw types.
 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Winston Gutkowski wrote:
It's really got nothing to do with that. Your declaration assignment has to provide a type on both sides of the '=', viz:
public final ArrayList<JComponent> aList = new ArrayList<JComponent>();
or, in version 7 or later:
public final ArrayList<JComponent> aList = new ArrayList<>();

Now that you mention it, Winston, I'm surprised the compiler accepted my code without at least the diamond operator, but it did (maybe because I'm using Java 8?). Adding "<>" or "<JComponent>," however, makes no difference to the problem I'm seeing.
 
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:
In your second example, you're declaring ListHolder to be a generic type. This means that holder is of a raw type (since you didn't specify the generic type argument when you declared it). This might break all the generics that go on inside of it, which would lead the compiler to treat aList as a raw type as well.

Indeed, when you declare holder as a ListHolder<String>, it does work.

By gum, you're right! Even this works:



What is weird though, is that you can first assign aList to a temporary variable of type ArrayList<JComponent>, and then use the iterator on it without problems. This is very inconsistent behavior.


Great Ghu, you're right! This works:




Regardless, never use raw types.


Well, you have my full attention with that advice. Why not use them? (I'm not being argumentative here; I'm new to these things, so all advice is appreciated.)
 
Stephan van Hulst
Saloon Keeper
Posts: 7969
143
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Stevens Miller wrote:Now that you mention it, Winston, I'm surprised the compiler accepted my code without at least the diamond operator, but it did (maybe because I'm using Java 8?).


It's because it's legal, but ill-advised. If you compile from the command line, or edit some of your IDE hints, it will actually warn you of this exact problem.

As for why you should never use raw types, it's because it completely undermines the compiler's ability to do type checking for you. When you use raw types, you can do stuff like this:

If list was declared as a List<Object> or even a List<?> instead of just a raw List, you would get a compiler error (as you should).
 
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:As for why you should never use raw types, it's because it completely undermines the compiler's ability to do type checking for you.


Ah, I see. Thanks! Going to read Chapter 12 of Core Java 9th ed. now, with that in mind.
 
Stephan van Hulst
Saloon Keeper
Posts: 7969
143
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
Have a cow for bringing this unusual case to our attention
 
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:Have a cow for bringing this unusual case to our attention

Hey, thanks, Stephan! I feel a bit guilty for winning a cow with bad coding technique, but it is much appreciated, nonetheless.
 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
For the sake of anyone who reads this in the future, I've taken out the ArrayList reference and replaced that class with one of my own, in the hope that it eliminates any confusion as to where the issue might arise. Also, I was wrong earlier to say "aList" was being taken as type Object. The Iterator next() method was being taken to return an object of type Object.

Here's my rewritten example:



It may be a better way to look at the problem to think about why it goes away when "ListHolder" is replaced with "ListHolder<?>" at Line x:



 
Stevens Miller
Bartender
Posts: 1445
30
C++ Java Netbeans IDE Windows
  • Likes 1
  • Mark post as helpful
  • send pies
  • Quote
  • Report post to moderator
And, finally(?), the JLS confirms this is not a bug:

The type of a constructor (Section 8.8), instance method (Sections 8.4, 9.4), or non-static field (Section 8.3) M of a raw type C that is not inherited from its superclasses or superinterfaces is the raw type that corresponds to the erasure of its type in the generic declaration corresponding to C.


In other words, it appears that generic members of raw types are, themselves, raw, like it or not.
 
  • Post Reply Bookmark Topic Watch Topic
  • New Topic
Boost this thread!