public class GenericDemo<E extends GenericDemo> {
    E innerE;

    public E doStuff(E e, GenericDemo<E> e2) {
        //insert code here
        }

    public E getE() { return innerE; }
}

The options are:
1. return e
2. return e2.getE()
3. return e2
4. return e.getE()

Option (1) and (2) compile fine. Option (3) and (4) give compile error.

  1. Precisely, for e2 says, incompatible types. Found: GenericDemo<E>. Required: E.
    My doubts is that, for e2, isn't E that is required, part of GenericDemo<E> which is e2's type ?

  2. For return e.getE() says, incompatible types. Found: GenericDemo. Required: E. Here I'm first confused as to how the return type is determined for a method invocation. getE() according to method signature returns E. But invoking it through e.getE() returns GenericDemo?? How is that happening? And even variable e is of the type E? So where is GenericDemo coming from when compiler says found: GenericDemo?

  3. And how is option(2) e2.getE() returning E that is actually compiled fine?

I hope I'm able to convey my doubts correctly.

Wow! What evil genius created that example? I suspect the answer will be clearer if you have a large cup of strong coffee then work through what that code looks like after after type erasure, considering different parameters that could be passed to doStuff at runtime (whose exact details may be unknown at compile time). If you work it out, please post the answers here.

Hahaha :) Well it's Kathy Sierra and Bert Bates. These guys are the main duo who make questions for the Oracle's Java Programmer Certification Test. So it is from their mock test book.

James I found an answer on some random site. But I could not understand it. Here it is.

The parameterized type for Hose is E, and you have defined E to extend Hose, however you have not defined for which type it should extend Hose.

Hence, for the class E, the method E getE(); is different depending on for which type Hose is extended. As it currently stands you have extended it for its raw type, without a parametrized type.

This is hard to explain I notice... consider this, instead of your current class definition of Hose, you use:
public class Hose <E extends Hose<E>> {

This will make your code compile. What I tried saying above is that for the class definition I just provided, the method:
public E getE() {

.. will indeed return a class of the same type, both from class Hose and from class E. If you do not define the parameterized type for E in the class definition for Hose, as I have done above, then there is no telling what the method would return when used from the class E.

P.S. Above is the entire answer I have copied from that site. Nothing I have written on my own.
The class is renamed to Hose here instead of GenericDemo but it's that very same program in question. Could you explain in layman terms what is being explained above please?

Particularly what is meant in the very first line "however you have not defined for which type it should extend Hose"

Edited 4 Years Ago by rahul.ch

Rahul - sorry, I'm just going out and won't be back until tonight some time, so no chance to study that. Hopefully someone else can help
J

Hey no problem James :) you have helped me on tons of occasions so thank you :)

Looks like the answer is in the difference between the raw type GenericDemo and the parametised type GenericDemo<E extends GenericDemo>
E extends GenericDemo but that does not automatically mean it extends GenericDemo<whatever>
That's about as much effort as I'm prepared to put into this until someone gives me an example of how you would encounter it in the real world :)

It is as JamesCherrill said. I'll try to explain each of your question as best I can. To simplify, let's assume there's a class called NewGenericDemo<E> that derives from GenericDemo<E>. K, here we go:

  1. In your class, E refers to a class which dervices from GenericDemo. When it does not have a parameter, it is equivalent to GenericDemo<GenericDemo>, but it could also be NewGenericDemo<GenericDemo> since that still derives from GenericDemo (the compiler can't be sure, it's implmentation specific). So if E is NewGenericDemo, the compiler cannot implicitly cast GenericDemo<NewGenericDemo> (e2) to NewGenericDemo<GenericDemo> (E), regardless of generic parameters, since GenericDemo is the super class of NewGenericDemo.

  2. Let's again assume E is NewGenericDemo. Your return type will expect that type; however, e will be NewGenericDemo (which is equivalent to NewGenericDemo<GenericDemo>, so e.getE() would return a type of GenericDemo. Again, this can't be implicitly casted.

  3. Since you are specifying that e2 is GenericDemo<E>, e2.getE() will return the type E, which is what the return type is.

Hope that helps. Let me know if you need me to clarify. I wasn't trying to disprove in a general sense, just trying to show one case where it wouldn't work. And compilers generally can't/won't work with uncertainty.

Edited 4 Years Ago by nmaillet

Let's start with why option 3 doesn't work. Generic types in Java are not covariant i.e. List<Integer> is not a List<Number>. Consider the following example:

List<Integer> list = Arrays.asList(1);
List<Number> numList = list; // doesn't work actually but assume it does
numList.add(Double.valueOf(1.0)); // Double is a Number
Integer i = list.get(1); // KABOOM!!

This above explanation forms the basis of the problem persented here. You just can't pass around GenericDemo instances of different parametric types and expect them to operate seemlessly with your API.

Since things can get complicated pretty quickly with the class definition you posted, let's create a new trivial minimal example to reproduce your problem:

class Hose<E extends Number> {
    // assume constructor

    public E doStuff(E e, Number e2) {
        return e2; // doesn't work
    }
}

What's the problem here? Isn't E a type which "extends" or is always equal to or a sub-type of Number. Well, the problem comes up with you try to use the result of doStuff method. Here is one usage:

Hose<Integer> h = new Hose<Integer>(1); // E now is Integer
Integer i = h.doStuff(2, 2.0); // BOOM! I'm expecting Integer but go 2.0

To get around this, here is what we can do:

public <T extends Number> T doStuff(E e, T e2) {
    return e2;
}

Now, since the compiler knows that the return type T is something which is a Number but can be any sub-type, you are able to do something like:

Hose<Integer> h = new Hose<Integer>(1); // E now is Integer
Number d = h.doStuff(2, 2.0); // No worries, we are covered

The fourth option is a bit easy to explain. If you will look at the class definition, it says extends GenericDemo. But GenerciDemo is a generic type(duh!). Since we don't specify a type parameter, it defaults to a "raw" type i.e. a generic type with no type information specified. Since the getE method actually works based on the type information captured by E and E in our case is a "raw" type with no generic information, it fails to compile. If you change your definition to class GenericDemo<E extends GenericDemo<E>>, it should work out fine because now the type captured by E is now GenericDemo<E> which itself has E as the type parameter.

Edited 4 Years Ago by ~s.o.s~

This article has been dead for over six months. Start a new discussion instead.