views:

55

answers:

3

Hi there!

Within my code a have the following abstract superclass

public abstract class AbstractClass<Type extends A> {...}

and some child classes like

public class ChildClassA extends AbstractClass<GenericTypeA> {...}

public class ChildClassB extends AbstractClass<GenericTypeB> {...}

I'm searching for an elegant way how I can use the generic type of the child classes (GenericTypeA, GenericTypeB, ...) inside the abstract class in a generic way.

To solve this problem I currently defined the method

protected abstract Class<Type> getGenericTypeClass();

in my abstract class and implemented the method

@Override
protected Class<GenericType> getGenericTypeClass() {
    return GenericType.class;
}

in every child class.

Is it possible to get the generic type of the child classes in my abstract class without implementing this helper method?

BR,

Markus

A: 

No, it is not possible, because the generic type information is erased during compilation, so it is not available anymore during execution.

Apart from implementing the method in each subclass as you did, the only option would be to pass the concrete class token to the base class (e.g. in the constructor), so that getGenericTypeClass can return it.

Update: on type erasure:

When a generic type is instantiated, the compiler translates those types by a technique called type erasure — a process where the compiler removes all information related to type parameters and type arguments within a class or method. Type erasure enables Java applications that use generics to maintain binary compatibility with Java libraries and applications that were created before generics.

For instance, Box<String> is translated to type Box, which is called the raw type — a raw type is a generic class or interface name without any type arguments. This means that you can't find out what type of Object a generic class is using at runtime. The following operations are not possible:

public class MyClass<E> {
    public static void myMethod(Object item) {
        if (item instanceof E) {  //Compiler error
            ...
        }
        E item2 = new E();   //Compiler error
        E[] iArray = new E[10]; //Compiler error
        E obj = (E)new Object(); //Unchecked cast warning
    }
}
Péter Török
Mhhh, but when I use you solution I have to defined a corresponding constructor in each sub class. So therefore I'm only moving my problem.Can explain more detailed why the generic type information is not available during the execution of the code?
Markus
Not true. When an implementation binds the type parameter to some type, that information is still stored in the class object. I.e., naikus' solution above does work.
waxwing
@Markus, see my update; @waxwing, oh well, with reflection...
Péter Török
Thanks for your further information. That topic was completely new for me. So I learned something today :-)
Markus
A: 

I'm not sure I fully understand your question - <Type> is the generic type of the subclass, even when it's being expressed in the abstract class. For example, if your abstract superclass defines a method:

public void augment(Type entity) {
   ...
}

and you instantiate a ChildClassA, you'll only be able to call augment with an instance of GenericTypeA.

Now if you want a class literal, then you'll need to provide the method as you indicated. But if you just want the generic parameter, you don't need to do anything special.

Andrzej Doyle
Sorry, I don't really got your point. Type is extended by the the generic types of the sub classes. Therefore it is possible to cast the type of the sub class back to Type and to use it inside the abstract super class.
Markus
@Markus - Now *I* don't understand your second sentence, I don't think it makes sense. You can't cast a type parameter in any sense; in the class definition(s) it's merely a placeholder pointing to the type you actually create an object with. And the subclasses don't *extend* type, they provide a concrete bound for it. So when the methods in `AbstractClass` are being invoked for an instance of `ChildClassA`, `type` **is** `GenericTypeA`.
Andrzej Doyle
+4  A: 

I think its possible. I saw this was being used in the DAO patterns along with generics. e.g. Consider classes:

public class A {}
public class B extends A {}

And your generic class:

  import java.lang.reflect.ParameterizedType;
  public abstract class Test<T extends A> {

     private Class<T> theType;

     public Test()  {
        theType = (Class<T>) (
               (ParameterizedType) getClass().getGenericSuperclass())
              .getActualTypeArguments()[0];
     }

     // this method will always return the type that extends class "A"
     public Class<T> getTheType()   {
        return theType;
     }

     public void printType() {
        Class<T> clazz = getTheType();
        System.out.println(clazz);
     }
  }

You can have a class Test1 that extends Test with class B (it extends A)

  public class Test1 extends Test<B>  {

     public static void main(String[] args) {
        Test1 t = new Test1();

        Class<B> clazz = t.getTheType();

        System.out.println(clazz); // will print 'class B'
        System.out.println(printType()); // will print 'class B'
     }
  }
naikus
I don't understand what the code in the constructor of Test really does, but it worked. Now I don't have to use the helper methods any longer. Thanks :-)
Markus
It uses reflection. First it gets the Generic superclass (java.lang.reflect.Type) of the class, in our case it will never be null and we know that it is a ParameterizedType, so we cast it and call the method getActualTypeArguents that returns an array of the actual type arguments e.g. B, of the type.
naikus
Ahhh, thanks for your explanation.
Markus
Be aware that creating either a `class Test2 extends Test1` or a `class TestFail<T> extends Test<T>` will break the above code.
ILMTitan
Good point @ILMTitan
naikus