wood burning stoves 2.0*
The moose likes Java in General and the fly likes What is wrong with Generics? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of Murach's Java Servlets and JSP this week in the Servlets forum!
JavaRanch » Java Forums » Java » Java in General
Bookmark "What is wrong with Generics?" Watch "What is wrong with Generics?" New topic
Author

What is wrong with Generics?

Volodymyr Levytskyi
Ranch Hand

Joined: Mar 29, 2012
Posts: 505
    
    1

Hello!

I have class DVTooltip as:

I want to have single instance of it like this :

Then any method that uses generic type T doesn't compile because class DVTooltip has T as generic type but single instance dvTooltip has <? extends DatabaseModel>.
For instance DVTooltip has method :
void ADD(T t){ . .................................}
where T is <T extends DatabaseModel>.
But single instance dvTooltip that calls this method ADD has its generic type as <? extends DatabaseModel>.

But if I try to make single instance dvTooltip have generic type T it fails telling :
Cannot make a static reference to the non-static type T

How to be here?


True person is moral, false is right!
Stephan van Hulst
Bartender

Joined: Sep 20, 2010
Posts: 3573
    
  14

Hi Volodymyr.

Please post an SSCCE to demonstrate the problem.
Volodymyr Levytskyi
Ranch Hand

Joined: Mar 29, 2012
Posts: 505
    
    1

I have already explained everything and if you read at least you would know.

The problem is that static fields cannot contain generic types of class. This is not possible.
Chan Ag
Bartender

Joined: Sep 06, 2012
Posts: 1000
    
  16
Volodymyr Levytskyi wrote:I have already explained everything and if you read at least you would know.

The problem is that static fields cannot contain generic types of class. This is not possible.


So do you still have a question? Your SSCCE does not show how you have coded the following parts. You might want to post if you are still looking for some kind of a help?

For instance DVTooltip has method :
void ADD(T t){ . .................................}
where T is <T extends DatabaseModel>.
But single instance dvTooltip that calls this method ADD has its generic type as <? extends DatabaseModel>.


Chan.


Jesper de Jong
Java Cowboy
Saloon Keeper

Joined: Aug 16, 2005
Posts: 14074
    
  16

Volodymyr Levytskyi wrote:But if I try to make single instance dvTooltip have generic type T it fails telling :
Cannot make a static reference to the non-static type T

You will get that if you do this:

Note that dvTooltip is static. That means there's only one instance of that variable for all instances of the class DVTooltip. Note that you can have multiple instances of DVTooltip with different concrete classes for the type parameter T. For example, you can have a DVTooltip<SomeDatabaseModel> and a DVTooltip<AnotherDatabaseModel>. There's not one shared type T that fits for the static member variable.

If there's still a problem, you have to tell us what's in the ADD method.

Java Beginners FAQ - JavaRanch SCJP FAQ - The Java Tutorial - Java SE 7 API documentation
Scala Notes - My blog about Scala
Volodymyr Levytskyi
Ranch Hand

Joined: Mar 29, 2012
Posts: 505
    
    1

This does not compile:

But I cannot replace generic type of demo instance with generic type T.
Piet Souris
Ranch Hand

Joined: Mar 08, 2009
Posts: 419
    
    5
Constructions like

don't work as you would think. For instance, suppose you have
a Shape class, and extensions Rectangle and Circle.

You would then have this possibility:



So Java doesn't allow these things.
You could try however:

I tried it and it works. But what exactly that would mean, I'm not sure.

Greetings,
Piet


Chan Ag
Bartender

Joined: Sep 06, 2012
Posts: 1000
    
  16
Volodymyr Levytskyi wrote:This does not compile:

But I cannot replace generic type of demo instance with generic type T.


This is how we need to write the add method.



Have you tried it?

Chan.
Volodymyr Levytskyi
Ranch Hand

Joined: Mar 29, 2012
Posts: 505
    
    1

Yes @Chan. This is what I do for now.

But this is applicable only for single method so there is no need to have generic type T on class Demo1.
Raymond Tong
Ranch Hand

Joined: Aug 15, 2010
Posts: 230
    
    2


For the first case, list could be List implementation of child class of Number
i.e. Number, Integer, Double, etc
which could not hold Integer (Double List could not hold Integer)

For the second case, list could be List implementation of parent class of Number
i.e. Number, Object
which could hold Integer (Object List could hold Integer)

If you search PECS Java Generics you may have more information.
Effective Java by Joshua Bloch on YouTube around 5:00
Basically, when a variable Produce item, use Extends
when a variable Consume item, use Super

In your case, it is going to Consume some value, it needs to be Super.
Volodymyr Levytskyi
Ranch Hand

Joined: Mar 29, 2012
Posts: 505
    
    1

Thanks @Raymond for this excellent reply!
Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 7492
    
  18

Volodymyr Levytskyi wrote:Thanks @Raymond for this excellent reply!

However, the main problem you still have (as I believe somebody already pointed out) is that your definition is static. The problem with ? super ... is that it then allows anything in the hierarchy, including Object.

Now sometimes that's what you want, but sometimes it isn't.

In your particular case (I think), the problem is easily remedied by propagating the extends out to the class - as I believe you already tried. So, using your code from above:and now you don't even need the field definition. However, if you simply want your class to be able to accept ANY DatabaseModel object, it's even simpler, and you CAN keep it static:
private static final DVTooltip<DatabaseModel> dvTooltip = new DVTooltip<>();

But it sounds to me like you're overthinking this.

It should probably be added that Generics is NOT a 100% solution because, unlike in C#, typed classes are not first-class objects. So, for example, there are several issues when combining generics with arrays.

However, if you keep things simple, it IS a 90% solution, and saves a lot of unnecessary casting.

Winston

Isn't it funny how there's always time and money enough to do it WRONG?
Articles by Winston can be found here
Piet Souris
Ranch Hand

Joined: Mar 08, 2009
Posts: 419
    
    5
Winston Gutkowski wrote: The problem with ? super ... is that it then allows anything in the hierarchy, including Object.

No, it doesn't.

Look at this code:

That's why I wrote that the semantics of something like "ArrayList<? super or extends>" is unclear to me.
But why OP doesn't simply use a concrete class instead of this <? extends T> is also unclear to me.

Greetz,
Piet
Matthew Brown
Bartender

Joined: Apr 06, 2010
Posts: 4338
    
    7

Piet Souris wrote:
No, it doesn't.

Look at this code:

Winston did say "in the hierarchy". I think he meant that to refer to ancestors, not classes on another branch of the inheritance tree.

I agree with your point about not necessarily needing the wildcard, though. I'd always try to design not using them as a first preference, and it's not clear to me that one is needed or helpful here.
Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 7492
    
  18

Piet Souris wrote:No, it doesn't.

I'm afraid it does.

Look at this code:

I'm surprised that you actually got that code to compile, since wildcards aren't supposed to be allowed on the right-hand side of an assignment statement, let alone with the new keyword; but I have to admit to not having used the diamond operator yet.

It's a common misconception that wildcards mean "any". They mean "unknown"...and ONLY "unknown". The actual object WILL have a specific type; the '?' just says that the compiler doesn't know what it is. And the super/extends qualifier simply constrains the type to what it might be (and super includes Object).

HIH

Winston
Matthew Brown
Bartender

Joined: Apr 06, 2010
Posts: 4338
    
    7

Winston Gutkowski wrote:I'm surprised that you actually got that code to compile, since wildcards aren't supposed to be allowed on the right-hand side of an assignment statement, let alone with the new keyword; but I have to admit to not having used the diamond operator yet.


It's safe because of type erasure. Since at runtime it doesn't actually matter which generic type is chosen, all the compiler needs to worry about is that there is a generic type that's compatible. And there is at least one: P.
Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 7492
    
  18

Matthew Brown wrote:It's safe because of type erasure.

I should add that I just tested an assignment like Piet's with:
ArrayList<?>, ArrayList<? super Number> and ArrayList<? extends Number>
(the last of which is, I think, what you were thinking of)

and I get the same compiler error every time:
Cannot instantiate type ArrayList<?[ ...]>

Winston

---------------------------------------------------------------------------
[Edit: Sorry folks. I edited this post instead of creating a new one, so here's my original reply to Matthew]
Matthew Brown wrote:It's safe because of type erasure. Since at runtime it doesn't actually matter which generic type is chosen, all the compiler needs to worry about is that there is a generic type that's compatible. And there is at least one: P.

Erm...with super? I hate to say, but that doesn't make any sense at all; and if it does, it blows pretty much everything I've ever read about generics out of the window.

How can the runtime generalize on P if supertypes of P are allowed?

Please explain. Because if you can, I've a LOT of re-reading to do.
Matthew Brown
Bartender

Joined: Apr 06, 2010
Posts: 4338
    
    7

The runtime doesn't generalize, because the runtime ignores the generic types entirely. Type erasure means that no generic information about variables is used at runtime. So the only thing that matters is the type checking done by the compiler. But as far as the type checking is concerned, everything is based on the reference type - the ArrayList<? super P>.

So the following lines:
Both should compile down to the same byte code (though I haven't tested that). Which means that the diamond operator is safe.
Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 7492
    
  18

Matthew Brown wrote:Both should compile down to the same compiled code. Which means that the diamond operator is safe.

Sorry, but I have to disagree (and if it does work that way, then it's a huge error in my book).

The diamond operator is strictly a convenience to save you typing (or it damn well should be; otherwise I suspect we're going to see a LOT of questions like this).
So to my mind, Piet's assignment:
ArrayList<? super P> ar = new ArrayList<>();
is exactly the same as:
ArrayList<? super P> ar = new ArrayList<? super P>();

and THAT produces the compile error I showed above.

Winston
Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 37900
    
  22
If you simply use super you get a Collection which can hold all sorts of things. But super has its uses. If you want a List of things Comparable, you don't simply write List<Comparable>, because Comparable is itself parameterised. So it is Comparable to something. So we try List<Comparable <T>>, but the you can't tell what T means.
So you say it is a list of things which are comparable to T, but it is in T that you find the CompareTo method. So you call it List<T extends Comparable <T>>.
But what if it is not really comparable to T?Now, as things stand you can hardly say that RadianAngle is comparable to itself. That is comparable to Angle, and Angle is a superclass. So you are comparable to something not specified, but it is a superclass of RadianAngle. So your actual List becomes
List<T extends Comparable <? super T>>
…and you get some spectacular errors comparing 123° with 122 radians
Matthew Brown
Bartender

Joined: Apr 06, 2010
Posts: 4338
    
    7

Winston Gutkowski wrote:
Matthew Brown wrote:Both should compile down to the same compiled code. Which means that the diamond operator is safe.

Sorry, but I have to disagree (and if it does work that way, then it's a huge error in my book).

The diamond operator is strictly a convenience to save you typing (or it damn well should be; otherwise I suspect we're going to see a LOT of questions like this).
So to my mind, Piet's assignment:
ArrayList<? super P> ar = new ArrayList<>();
is exactly the same as:
ArrayList<? super P> ar = new ArrayList<? super P>();

and THAT produces the compile error I showed above.

Winston

Yes, that should be an error. Because it shows a fundamental misunderstanding of what the wildcard means, and so the compiler should flag it up.

I was about to admit that I believed it was safe (and I couldn't locate a definitive statement in the JLS either way), but I didn't know it would compile. But having just tested it, it works as I'd expect. I'd disagree that this is "wrong" - the fact that the behaviour is as I guessed it would be means that I don't believe it's misleading, and as I said it's definitely safe.

According to the warnings by IDE generates, List<? super Number> list = new ArrayList<>() infers the type as Object, and List<? extends Number> list = new ArrayList<>() infers the type as Number, which makes sense to me: it's taking the least specific possible for super and the most specific possible for extends.
Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 7492
    
  18

Matthew Brown wrote:According to the warnings by IDE generates, List<? super Number> list = new ArrayList<>() infers the type as Object, and List<? extends Number> list = new ArrayList<>() infers the type as Number, which makes sense to me: it's taking the least specific possible for super and the most specific possible for extends.

Now THAT I can understand (glad we got this cleared up; I had visions of having to read Gilad Bracha's paper all over again ).

I still say it's a bad choice though. I thought Java was all about simplicity?

Winston
Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 7492
    
  18

For those who haven't followed the all recent chatter, I'm going to re-state what I said in a previous post, because I think it's important:

It's a common misconception among beginners to Java generics that '?' means "any". It doesn't.

It means "unknown"...and ONLY "unknown".
The actual object WILL have a specific type; the '?' just says that the compiler doesn't know what it is.

The super/extends qualifier simply constrains the type to what it might be (and super does include Object).


As to the behaviour of the diamond operator (see Matthew's last post), all I can say is: I don't think it would have happened if Sun was still around.

Winston
Piet Souris
Ranch Hand

Joined: Mar 08, 2009
Posts: 419
    
    5
Matthew Brown wrote:According to the warnings by IDE generates, List<? super Number> list = new ArrayList<>() infers the type as Object (...)

That's what I thought too. But if you replace (in my code) line 12 by:

then you still get the same runtime error. So all this is beyond me and I never use (or have used) constructions like Volodymyr's.

And although Winston has his doubts about how Oracle implemented the diamond operator, I'm glad it's there. It does save a lot of typing!
Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 7492
    
  18

Piet Souris wrote:And although Winston has his doubts about how Oracle implemented the diamond operator, I'm glad it's there. It does save a lot of typing!

Then maybe you need to re-read, because that's what I thought it was all about. Matthew has plainly proved me wrong.

If he's right (and I have no reason to think otherwise, because he's a good egg, and very knowledgeable), the operator makes assumptions about wildcards which - while they may be consistent - are at odds with what I understand '?' to mean.

So, knowing what you know about generics, if you write:
ArrayList<? super P> ar = new ArrayList<>();
would you take it for granted that when you use that (diamond) operator, and only that operator, that assignment will actually be:
ArrayList<? super P> ar = new ArrayList<Object>();
as opposed to giving you a compiler error? I certainly wouldn't.

If that's the "choice" that was made for diamond operator behaviour, then it really bothers me, because it would appear that
(a) Oracle have abandoned Java's "keep it simple" ethos.
(b) They've forgotten why generics was created to begin with.

It would have been so much simpler if they'd just said:

The diamond operator is provided for use whenever you need to repeat character-for-character the contents of the outer "braces" of a generic type on the LEFT-hand side of an expression, in the "diamond" on the RIGHT-hand side.

and, I suspect, a lot easier to implement too; because then it's simply "syntactic sugar".

In C, it would be called 'pre-processing', but Matthew's post suggests that Oracle have decided that it needed a bit more "oomph" in order to call it an 'operator'. So in order to enable it, they gave you yet another thing to remember.

Bad move in my opinion.

Winston
Chan Ag
Bartender

Joined: Sep 06, 2012
Posts: 1000
    
  16
All, fantastic responses. Thanks. But I still don't get why the following compiles.



But the following does not.



I understand that Volodymyr Levytskyi wants his generic class to be parametrized such that the type parameter is-a DatabaseModel and he needs to pass a Column to his method.
So the only options he would have is either use



or he does this ( I understand both the options are different, or aren't they? ).



But shouldn't the following


also mean that T is anything that extends DatabaseModel considering the class is parameterized as 'T extends DatabaseModel'.

Or have I missed a post that has already answered this? Or am I possibly not getting something that has already been said?

Chan.

Edit : If it is just a syntax thing, then it's just too many rules to remember I'd say.
Raymond Tong
Ranch Hand

Joined: Aug 15, 2010
Posts: 230
    
    2

Chan Ag wrote:


Actually, the T defined in method1 is different T defined in class.
You can replace it with

Your code snippet with <T> works because it does not apply any constraint related to type T defined in class level.
Your code snippet without <T> does not because it requires type T, which could be itself or child classes of DatabaseModel which may not be Column.
Chan Ag
Bartender

Joined: Sep 06, 2012
Posts: 1000
    
  16
Thanks, Raymond. That partly answers it.

But why should the following give a compilation error.



Isn't the generic type supposed to provide me the exact functionality the Op wants to realize in his code?

What would you all say?

Chan.

Edit : Raymond, I just read the last para of your response again.
I get your point and yes that's a complete response.
Thanks. Now that explains why the other construction doesn't compile.
cool.
Chan Ag
Bartender

Joined: Sep 06, 2012
Posts: 1000
    
  16
I guess since

[strike]
[/strike]
already provides the functionality that Volodymyr wants to get in his code, the following is not allowed

[strike][/strike]

when we code method1 as follows.

[strike][/strike]

But this is just a guess.

Chan.


Edit : This is wrong. Please ignore this post.
Volodymyr Levytskyi
Ranch Hand

Joined: Mar 29, 2012
Posts: 505
    
    1

This is because of erasure of generic types used by java compiler :
For instance source code:

This source code is replaced by java compiler into :

As you see java compiler replaced T with its topmost bound - Comparable.

I found this explanation here
Volodymyr Levytskyi
Ranch Hand

Joined: Mar 29, 2012
Posts: 505
    
    1

I think that generic type <T extends DatabaseModel> for class works diffrently than <T extends DatabaseModel> for method.
And why is that?
Chan Ag
Bartender

Joined: Sep 06, 2012
Posts: 1000
    
  16
I just read Winston's first response again and now I see what I was missing. Great points, Winston and everyone else. Thanks.

I should probably code Volodymyr's class as follows.



Sorry for confusing everyone. :-)

By the way, thanks Volodymyr for the link. I'm still going through how the type erasure works for methods.
Chan Ag
Bartender

Joined: Sep 06, 2012
Posts: 1000
    
  16
Also I think it must be a very poor design to have a method that
doesn't take parameters but invokes a method that takes a generic type parameter inside a generic class.

It makes the generic class non generic, isn't it?
Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 7492
    
  18

Chan Ag wrote:I just read Winston's first response again and now I see what I was missing.

And just FYI, that line 7 of yours could be:
public class Demo1<T extends DatabaseModel> { ...

There's no doubt that Java generics can be tricky, but I've found that if you keep things simple (and, like everything else, a good thing to do is to write down what you want to achieve before you start adding tons of generics) it can be a real help. I've also found that when I start getting tied in knots, it's almost always because I've made a mistake, not generics.

There are a few thing to remember, like using:
<T extends Comparable<? super T>>
when you want to extend a Comparable, and remembering that '?' means "unknown", not "any"; but otherwise I find it pretty straightforward.

Winston
Chan Ag
Bartender

Joined: Sep 06, 2012
Posts: 1000
    
  16
Thanks all.

Sorry, I didn't mean to hijack this topic. Just I had similar questions that are now answered completely. I do realize that Volodymyr has a question, though.

Martin Vajsar
Sheriff

Joined: Aug 22, 2010
Posts: 3606
    
  60

Hah, Winston, you've made me install JDK7! Thanks!

I really think that you aren't being fair to Oracle in your assessment of the diamond operator. First of all, generics themselves happened in the good old times under the Sun, and they are faaaaaar from "simple". If they were simple, there wouldn't be Angelika Langer's FAQ and I wouldn't have to re-read it every half year or so. Diamond operator inherits the complications to some extent.

Secondly, instead of coming up with something new, Oracle designed the diamond operator to use type inference, a mechanism which already existed in Java (honestly I don't know if type inference had to be enhanced somehow due to the diamond operator - if it was, it changes things a little bit though). Do you really think it would be simpler to have two different set of rules, one for method invocations, and another for constructor invocations? I don't consider the question entirely closed, but I lean heavily towards the "let's not have two different mechanisms" camp.

Thirdly, the question itself is more complicated than the "left side" and "right side". Consider:

See? No assignment in sight, and we still have a diamond operator here. I was not able to come up with something truly interesting here, but something will soon pop out as soon as I start using Java 7 productively.

Just my two cents.
Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 7492
    
  18

Martin Vajsar wrote:Hah, Winston, you've made me install JDK7! Thanks!

No probs. I was a bit slow myself.

I really think that you aren't being fair to Oracle in your assessment of the diamond operator...

Actually, I'm probably just not fair to them, period. Don't like 'em, never will; and I think their database is crap (probably too much time spent forcing the square peg of 8i into Linux's round hole) - so I can't think of too many companies I'd want less holding Java's future in its hands. What's worse, I did like Sun - 15 years with their kit...even worked for them for a while; great bunch - so I'm bound to be biased.

Diamond operator inherits the complications to some extent...

Probably. I'm just not sure there was any need for it to. However, you're right: I really must read up a bit more about the diamond operator before I start slagging off Oracle too much. Hey, it's fun though.

Winston
Martin Vajsar
Sheriff

Joined: Aug 22, 2010
Posts: 3606
    
  60

<hijack>
Winston Gutkowski wrote:Don't like 'em, never will; and I think their database is crap (probably too much time spent forcing the square peg of 8i into Linux's round hole)

I came from exactly the opposite direction - I think their database is pretty good. However, I've experienced 8i for only a short while, and each of the subsequent ones was more pleasant to work with than the previous. Most importantly to me, they have excellent documentation, and most grievances I've seen on various forums can be at least partly attributed to not reading it at all. I do hate their networking stuff, though, and shy away from answering Listener and TNSNAMES stuff even here.

On the other hand, I too would think that they're not the best fir for Java. Don't know whom to wish as the caretaker, though.
</hijack>
Piet Souris
Ranch Hand

Joined: Mar 08, 2009
Posts: 419
    
    5
Volodymyr Levytskyi wrote:I think that generic type <T extends DatabaseModel> for class works diffrently than <T extends DatabaseModel> for method.
And why is that?

Back to the OP:
no, there is no difference. But if you use <T extends P> in the same class for your class definition AND for a method definition within that class,
then we are talking about different T's. Raymond Tong has already explained this. And depending on the contents of a particular class,
you may get a compile error.
Pat Farrell
Rancher

Joined: Aug 11, 2007
Posts: 4646
    
    5

Oh foo, I had such high hopes from the OP's subject line, I expected a thread on what is wrong with Generics, rather than what was wrong with the OP's specific use of Generics.

On the more general topic: what is wrong with Generics, their design and implementation?

1) a simple question takes four or five Bartenders to get a clear answer. And that answer is not clear.

2) There is a 700 page FAQ that tried to explain Generics

3) type-erasure is one of the most broken concepts that I've ever seen.
 
wood burning stoves
 
subject: What is wrong with Generics?
 
Similar Threads
FBN Design Brainstorm!
question in generic
instantiating a parameterized type
Ho to not use casting on this generics?
Class class