• 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

confused with generics

 
Greenhorn
Posts: 27
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hello, I've been searching for an answer in JAVA forum for many hours, but I didn't find it.
Can anyone explain me how does the code below works? Can you give an example that might help me ?

1. abstract class ABS<K extends Number>
2.{
3. public abstract <K> K useMe(Object k);
4. public abstract <K> ABS<? extends Number> useMe(ABS<? super K> k);
5. public abstract <K> ABS<? super Number> useMe(ABS<? extends K> k);
6. public abstract <K> ABS<K> useMe(ABS<K> k);
7.}

 
Saloon Keeper
Posts: 15491
363
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This class is pretty much bogus. All the methods' parameters have the same erasure, so it won't compile in the first place. On top of that, each method overrides the class signature's formal type parameter.

Unless you can tell us the context in which you found this code, and why it matters to you, I have no other advice for you than that you should forget about this snippet as quickly as possible.
 
Tom Mark
Greenhorn
Posts: 27
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Sorry. This code is from mock test and it should be:

Which are the valid method declarations?

abstract class ABS<K extends Number>
{ // insert code // }

1. public abstract <K> K useMe(Object k);
2. public abstract <K> ABS<? extends Number> useMe(ABS<? super K> k);
3. public abstract <K> ABS<? super Number> useMe(ABS<? extends K> k);
4. public abstract <K> ABS<K> useMe(ABS<K> k);

I know little about generics, but this syntax is too complicated for me.
Especially I don't understand who determines generic <K> in a method.
Can you give a simple example how it might work with types and references.
I'm just trying to understand who is who and where does all things comes from.
Thanks.
 
Stephan van Hulst
Saloon Keeper
Posts: 15491
363
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Only 1. should work. 2. does as well with my compiler, but I personally don't understand why. Maybe it's a bug.

What all these methods have in common is that their generic type parameter hides the class' type parameter. Each K in the method signature is *different* from the K in the class declaration. Now, the reason 2. 3. and 4. shouldn't work, is because when you call the method, you can declare K to be anything at all. Which means that the parameters of the three methods could respectively be ABS<? super Object>, ABS<? extends Object> and ABS<Object>, if you call the method with the actual type parameter Object:

In case 4. it's obvious why the code doesn't work. How can you have an ABS<Object> as a return type? ABS always has something that extends Number as its actual type parameter.
For cases 2. and 3. we have to look at the method arguments. What if we didn't use null?

2. would take an ABS<? super Object>. How is this possible? ABS<? super Object> is not within the bounds of the class declaration. The same goes for 3. ABS<? extends Object> is not within the bounds either. The generic type parameters should be at least a Number, and neither <? super Object> and <? extends Object> can guarantee this requirement.

Which leaves us with 1., which is valid. It simply takes an Object, and it promises to return a K. If you call abs.<Object>useMe(null), it simply promises to return an Object.
 
Tom Mark
Greenhorn
Posts: 27
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Sorry for my incompetence Stephan. I appreciate your comments, but I have syntax problems yet. Please, explain me.
I'm trying to understand how to read this code. For example, if class generic <K extends Number> is Integer?

Does it mean that public abstract <K> K useMe(Object k) becomes public abstract <K> Integer useMe(Object k) or else ?
And what about <K> in the method. Who determines it?



I have the same problem with another methods. For example, who determines <K> , ABS<? extends Number> etc. ?
I just don't understand what is written here.


Sorry for my English. This is not my native language.
 
Stephan van Hulst
Saloon Keeper
Posts: 15491
363
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
A generic class' actual type argument is determined the moment you create an instance of it. You should be familiar with the following syntax:
If a method declares its own generic type parameters, the actual type argument of the parameter will be determined at the time you call the method.
Here T is determined separately for each method call. We call identity() using a String literal, so the return type of the method will also be a String. This is called type inference. The compiler determines what T stands for based on the way you call the method. Compare:
The compiler infers from the formal type of the argument you're passing (Object) that T should be Object. So the method will return an Object. The compiler complains, because you're trying to assign an Object reference to a String variable.

Now, type bounds make things a little bit more interesting. When you have a formal type parameter, you can specify additional restrictions that the actual type argument has to adhere to.

Let's say you have the following method:
You now no longer call identify() on arguments of the Object or String type, because whatever the argument is, it has to be at least a Number:
When you call a generic method, you can even explicitly state what the type argument is, regardless of the type of the arguments you pass:
In this example, even though the argument type is Integer, we have explicitly stated that the type argument should be a Number.

Now, the example you have given makes things a little bit more confusing. The problem is that the class declares a type parameter K, but the individual methods also declare their own type parameters K. It's important to note that these are two completely different type parameters! The method's version of K 'hides' the class' version of K. Just to make it more easy for yourself, rename the class' version to M.

Now, how should you read a method declaration such as:
Here's what it says: useMe expects one argument of type ABS<foo>, where foo is something... anything... that happens to extend K. The method will then return an ABS<bar>, where bar can be something... anything... that happens to be a supertype of Number. Intuitively, this method will always return either an ABS<Number> or an ABS<Object>, but you won't know which of the two.
So what is K then? Like I explained earlier, if you don't state it explicitly, the compiler will infer K as the formal type of the argument you pass to it. If you pass an ABS<Double> then K will be Double.

Maybe you will start to see why 2. 3. and 4. shouldn't compile. None of the methods puts a bound on what K can be. So K could theoretically be an Object, or a String, or a Dog, or a House. This means that methods 2. 3. and 4. would theoretically accept an argument of the type ABS<Dog>. But now finally the class' type parameter, M, comes into play. How can we possibly have something like an ABS<Dog>? Whatever the type argument of ABS is, it should at least extends Number. This is why none of the three methods compile.

1. On the other hand compiles just fine, because it doesn't make any illegal declarations. It simply states that it has a generic type parameter K, it will accept an argument of type Object, and it will return a value of type K, whatever K happens to be. What is K? We can state it explicitly, like so:
We explicitly state that the type argument for this method call will be House. So the method promises to return a House instance, which we can then assign to our House variable. The argument the method takes can be anything, because it simply accepts Object. In this case, we pass a new Object(), but we also could have passed a String, or a Dog, or anything else.
 
Tom Mark
Greenhorn
Posts: 27
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you. You really helped me though I have some more question regarding generics.
You say that 2 and 3 lines doesn't compile, but mock test tells that these methods are valid.




For me they are valid too. As I understand abstract class ABS<K extends Number> means that K can be:
Number
Integer
Float
Double
etc.

ABS<? super Number> means that we can have
ABS<Number> (this one fits)
ABS<Object>

Next. If we have useMe(ABS<? super K> k) then K could be Integer, Double, etc. or a Number, because <? super Integer> generates ABS <Integer> or ABS <Number> (it fits too)


What do you think?

 
Tom Mark
Greenhorn
Posts: 27
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Also, why this method is valid according to the test?
I thought that K declared in the method is not the same K declared in the class.


 
Stephan van Hulst
Saloon Keeper
Posts: 15491
363
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tom Mark wrote:ABS<? super Number> means that we can have
ABS<Number> (this one fits)
ABS<Object>

Next. If we have useMe(ABS<? super K> k) then K could be Integer, Double, etc. or a Number, because <? super Integer> generates ABS <Integer> or ABS <Number> (it fits too)


In both cases the bound of K doesn't stop the type argument from being Object, so both the return types and the arguments of the two methods could potentially be an ABS<Object>, which is illegal. The fact that the method is able to use an ABS<Number> in some cases, doesn't mean that it should allow an ABS<Object>. The signature should be valid for *all* possible values of K.

My suspicion is enforced by the following snippet of code I wrote:
This code won't compile, because there's no way it can guarantee that it will return a legal ABS. In fact, the only value you can return that will make the code compile is null.
 
Stephan van Hulst
Saloon Keeper
Posts: 15491
363
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tom Mark wrote:Also, why this method is valid according to the test?
I thought that K declared in the method is not the same K declared in the class.



In this case it is. Note that useMe() doesn't redeclare K. So it doesn't hide the original K declared in the class signature. It simply declares V and expresses its bounds in terms of K.
 
Tom Mark
Greenhorn
Posts: 27
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Ok. But lets go back to the first method. This will be my last question.
As I understand, the method below doesn't redeclare K too.
It uses K (not <K>) declared in the class signature. Am I right?



You mention earlier that we can state <K> explicitly. So it's clear.
But what happens if don't state explicitly. For example:



What will be the return type?

 
Stephan van Hulst
Saloon Keeper
Posts: 15491
363
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Actually it does redeclare K. Type parameters are declared using the angle-bracket notation: <K>. If the method wants to use the K declared by the class signature, it should omit the declaration in the method signature, like so:
You can compare it to declaring a member variable:
Here the class already declares a variable rufus, and the method redeclares the variable, shadowing it. The same is true for generic type parameters. Except type parameters are not variables that hold object references, but they hold actual type arguments instead.
In case 1., the original K declared by the class still exists (just like our rufus member variable does), but it's hidden by the K declared in the angle brackets of the useMe method, just like our member variable rufus was hidden by the rufus declared in the place() method.

As for your second question. When the compiler can't infer the type from the method arguments, it will infer the type from the formal type of the variable you're trying to assign the return value to, or else it will just use the upper bound of the generic type parameter.
Here K will be of type Boolean.

If the method doesn't have any arguments and no return type, then it doesn't matter, because what's the point of a generic method that takes no arguments and returns no value?
 
Tom Mark
Greenhorn
Posts: 27
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you Stephan for your time.
You helped a lot.
 
reply
    Bookmark Topic Watch Topic
  • New Topic