views:

106

answers:

4

Here, I have an abstract class:

abstract class A<E extends A> {
    abstract void foo(E x);
}

Here's a class that extends A:

class B extends A<B>{
    void foo(B x){}
}

And here's another (E is B here on purpose):

class C extends A<B>{
    void foo(B x){}
}

Both of those classes are valid, and the reasoning for that makes sense to me.

However what confuses me is how this could possibly be valid:

class D extends A{
    void foo(A x){}
}

Since when are generics optional like that? I thought the extending class (subclass) of A would be required to specify an E?


Edit:

The two answers received so far say that E defaults to an Object if no argument is provided.

Alright - well then why doesn't this work (below)?

class D extends A<Object>{
    void foo(Object x){}
}
+3  A: 

Generic type arguments are optional. If you use a class with generic arguments without those argument it is called a raw type. So yes you can use A without generic arguments.

See Raw Types from the Java Generics FAQ.

cletus
(+1) If it's optional, what do the types of parameters in methods with `E` 's as parameters default to? The Superclass itself? I mean this seems really odd.
Cam
They simply become `Object`s, which is what they are with type erasure anyway.
cletus
@cletus: Hm. How can I make a generic argument non-optional? I mean what's very strange is that although it defaults to an `Object` if no argument is provided, providing an `Object` as an argument for the example above doesn't actually work!
Cam
@incrediman you can't make it mandatory. You need to understand how Java generics work: they're just *syntactic sugar* for casting `Object`s. They're (mostly) not retained at runtime.
cletus
Alright, fair enough :)
Cam
It actually becomes `A` in this case. (Note: Using the naming standard for generic parameters for class names does not help clarity!)
Tom Hawtin - tackline
+3  A: 

When generics were added, Java wanted to keep backward compatibility with previous versions. So, generics are options optional, AND the generic info is used at compile time, but completely dropped at runtime (it's called type erasure).

CWF
Interesting (+1). So I can't make a generic parameter non-optional?
Cam
+1  A: 

well then why doesn't this work (below)?

class D extends A<Object>{
    void foo(Object x){}
}

Because you defined class A<E extends A>; you've raised the floor to A instead of Object. This works fine:

class D extends A<A>{
   void foo(A x){}
}

You can use reflection/bytecode decompilation to examine what the generic types/methods actually compile to at run time.

import java.lang.reflect.*;

public static void listMethods(Class<?> klazz) {
    System.out.println("Declared methods for " + klazz);
    for (Method m : klazz.getDeclaredMethods()) {
        System.out.println(m);
    }       
}
public static void main(String args[]) {
    listMethods(A.class);
    // Declared methods for class A
    // abstract void A.foo(A)
}
polygenelubricants
Alright, that makes sense now - thanks. But I simply do not understand how type erasure causes those classes to become Objects after compilation (as suggested by the other answers) - wouldn't they become A's?
Cam
Oh. Yeah nevermind - I think I get it now! Thanks.
Cam
A: 

Sidestepping a bit from the original question here but this declaration bugs me. Not to say that it is fundamentally wrong but it does raise question here as to the use of generics.

abstract class A<E extends A> {
    abstract void foo(E x);
}

looks to me like a generic object that does operations on instances of itself, the only possible use of generics would be so that the parent instance can auto-cast to one of it's child decided at compile time... One way to make method signature declared in the parent auto-cast in child objects to the child-itself...

Maybe I am just thinking too much for a Monday morning !!

Newtopian
Can you rephrase that? Not really sure what you mean by "the only possible use of generics would be (_if what?_) so that the parent instance can auto-cast to one of it's child decided at compile time"
Cam
Yeah... I guess I needed my coffee yesterday !!! Basically I just found weird that the template class would only accept itself or descendants of itself as a type argument. I was wondering why would someone want such class structure, what would be the possible use of this. However I do realize that this code is to demonstrate something else entirely so it is quite possible that that never was the intent.
Newtopian
Yeah if that was actually the point of what I was doing I would probably have avoided generics all together and done this: `abstract class A{ abstract void foo(A e);}` instead.
Cam
yes, that would have been my first instinct too. However there are cases where you would want the child to override the parent functionality while keeping types returned to be the child's type and not the generic parent type. I was wondering if doing generics this way would allow us to override the return type as well while not breaking the Liskov substitution principle
Newtopian