3

Consider the following code:

// ...
public class BaseClass
{
    public BaseClass (int theParam)
    {
        // ...whatever...
    }
}
public class DerivedType
{
    // ...Content does not matter...    
}


// ...elsewhere:

public <ElemType extends BaseClass> boolean doIt (ArrayList<ElemType> target)
{
    ElemType newElem=new ElemType (5) ; // "Cannot instantiate this type"

    // ...other code does not matter...

    return true ;
}

// ..

How can I create an instance of type ElemType in doIt?

The construct shown yields the error indicated.

ElemType.newInstance does not exist, which surprises me.

I've read practically all FAQs, answers and googleable material, but I cannot find anything helpful.

EDIT: Yes I know reflection has its downsides, and is not the ultimate solution, for numerous reasons. The question is not "should I do it", but "how would I do it".

TheBlastOne
  • 4,291
  • 3
  • 38
  • 72
  • Where does `DerivedType` come into play? I do not see any references to that? – home Mar 13 '12 at 17:56
  • Well I would call doIt with an ArrayList instance somewhere, and with an ArrayList instance elsewhere. Since that does not contribute to the problem, I did not make up those calls in the sample code. – TheBlastOne Mar 13 '12 at 17:58
  • Read about Type Erasure [here](http://docs.oracle.com/javase/tutorial/java/generics/erasure.html) – Cagil Seker Mar 13 '12 at 18:01
  • see [this question](http://stackoverflow.com/questions/1090458/instantiating-a-generic-class-in-java) – jabu.10245 Mar 13 '12 at 18:02
  • possible duplicate of [Create instance of generic type in Java?](http://stackoverflow.com/questions/75175/create-instance-of-generic-type-in-java) – erickson Mar 13 '12 at 18:03
  • You've read everything but still don't understand type erasure? – erickson Mar 13 '12 at 18:04
  • I do understand that. I don't understand why I do not have access to the type information of ElemType via reflection. – TheBlastOne Mar 13 '12 at 18:10
  • That's due to type erasure. There is a trick that might allow you to work around it in special cases, called "super type tokens". Basically, if you create a type that implements a generic interface or extends a generic type with concrete type parameters, you can determine those parameters at runtime. [This is an excellent resource](http://www.angelikalanger.com/GenericsFAQ/FAQSections/TypeParameters.html#Can%20I%20create%20an%20object%20whose%20type%20is%20a%20type%20parameter?) that answers this and many other questions about generics. – erickson Mar 13 '12 at 18:14
  • I know that FAQ, and I consulted it intensively, but it does not contain a hint how to use reflection in this regard. For generic types, it covers this. For generic parameter types, it doesn't. I am just trying to understand why this is so. And yes I will look into super type tokens, thanks. – TheBlastOne Mar 13 '12 at 18:21
  • @TheBlastOne I updated my answer according to your comment; please see if it suits you... – Cagil Seker Mar 13 '12 at 18:53

4 Answers4

3

Please consider that generic-informations are erased by the compiler at compile-time and are replaced with object. Internally generics is just casting from and to java.lang.Object.

This also why it's difficult to obtain generic information at runtime, even though it's possible.

See here: Google.

On a persopnal note: If you need to do anything like this, it is usually bad design. I was at this situation a couple of time, but I found a better solution every time :). So just consider if you really want such a dirty hack in your code.

Edit: Regarding to the comment section a more detailed explanation is needed.

Reflection in general should be used with caution anyway, since from a software engineering point-of-view it is considered bad design. Why? It might introduce some hard to find bugs, since reflection alters the natural flow of your application and uses informations which are not always visible to you at development-time. This screams out for unexpected behavior.

And even though I have no formal proof for this, but I state that each time you need reflection, there is another solution for your problem (if generative software-development is an option ;)).

So in the end, in 99% of all cases reflection is nothing more then a dirty hack of a lazy programmar. This might be related to the fact that 100% of all programmars are lazy, but anyway.

Edit 2:

Since you want the code anyway:

abstract class Foo<T> {
    private Class<T> tClass;

    T field;

    public void bar(Class<T> clazz) {
        Type type = getClass().getGenericSuperclass();
        if (type instanceof ParameterizedType) {
                ParameterizedType paramType = (ParameterizedType)type;
                tClass = (Class<T>) paramType.getActualTypeArguments()[0];

                field = tClass.newInstance();
        }
    }
}

(taken from: Here)

Jan Gräfen
  • 314
  • 2
  • 12
  • I see. All I want is to refactor code which is repeated all over the place just because the elements I have in an ArrayList are descendants of some base class...that´s what generics are for. – TheBlastOne Mar 13 '12 at 18:02
  • The important thing is that doIt () does not always have to create a new instance, only sometimes. So it is not an option to let the caller pass a newly created instance -- I would through it away (i.e. not use it and let it garbage-collect) 99% of all times. – TheBlastOne Mar 13 '12 at 18:03
  • And note that if you google this, you find not only notes "don´t do it", but smart (?) solutions based on reflection. Including solutions for instantiating generic types via newInstance, getConstructor etc. I am wondering how one would do it (if there is no better way) using reflection in this case, which has a method type parameter. – TheBlastOne Mar 13 '12 at 18:08
  • As I said, it is possible and in very (VERY) rare cases it might be useful. But still, reflection should be avoided by any price, since it may lead to bugs which are difficult to track. I would suggest a factory in your scenario, since it encapsulates the repeating code, without the need for reflection. – Jan Gräfen Mar 13 '12 at 18:12
  • OK, so what would the oh-so-dangerous code using reflection code would look like? If I promise not to use it, and create a factory instead, can you name the reflection methods to use, and how? I mean, this is getting interesting. – TheBlastOne Mar 13 '12 at 18:17
  • so your answer is "You cannot use reflection on the generic method type. You must create a generic-typed class." Right? – TheBlastOne Mar 13 '12 at 18:29
3

As mentioned, type erasure of generic types does not allow that. But you can achieve what you want like this:

public class BaseClass {

  public BaseClass(int theParam) {
    // ...whatever...
  }
  public BaseClass() {  
  }          
}

public class DerivedType extends BaseClass {
}

And now doIt() method gets the class argument for reference:

public <D extends BaseClass> boolean doIt (ArrayList<D> target, Class<D> c)
{
    try {
        D newElem = c.getDeclaredConstructor(int.class).newInstance(5);
    } catch (Exception e) {}

    // ...other code does not matter...

    return true ;
}

And you should call it like this:

    ArrayList<DerivedType> testList = new ArrayList<DerivedType>();
    testList.add(new DerivedType());
    testList.add(new DerivedType());
    doIt(testList, DerivedType.class);

Hope that helps :)

Note that, one may really want to be hacky and get rid of the class parameter and try this:

 public static <D extends BaseClass> boolean doIt (ArrayList<D> target)
 {
    try {
        D newElem1 =  ((Class<D>) ((ParameterizedType) target.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).getDeclaredConstructor(int.class).newInstance(5);

    } catch (Exception e) { e.printStackTrace();}

    return true ;
    }
}

In fact I thought so before the second edit :) But this gets a "java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl cannot be cast to java.lang.Class" exception as you mention (I didn't see it because of an overlooked catch statement). In short, Java runtime system does not store the parameterized types (in favor of backwards compatibility; so this may change in the future).

So, it looks like it is not possible without 'touching' some class.

However, other than the mentioned methods, I can think of two more things. First, if both the BaseClass and the DerivedType 'D' class implement clone() method, you can get a clone of an object from the array and then use it:

         D o = target.get(0);

         D oNew = (D)((BaseClass)o).clone();
         target.add(oNew);

Polymorphism will take care of the rest :)

The second one is not a real 'solution', but can be used if all you want is a new instance for an array of objects parameterized by type. Type Erasure only happens for parameterized types, but it does not happen for basic arrays (arrays are reified in JVM). So if we have the freedom to change the signature of the method and working with arrays is ok, then the following would work:

    public <D extends BaseClass> boolean doIt(D[] target) {
    try {
        D newD = (D) (target.getClass().getComponentType().getConstructor(int.class).newInstance(8));
        target[0] = newD;

        // The following is optional, if we want to work with Collections internally
        List<D> l = new ArrayList<D>(Arrays.asList(target));
        l.add(newD);  


    } catch (Exception e) {
        e.printStackTrace();
    }
    return true;
}

Note: Super type tokens would not work for this problem if we cannot introduce new parameters. Please correct me if I'm wrong.

Cagil Seker
  • 333
  • 2
  • 9
  • Ah. +1. What if I would not pass the DerivedType.class to doIt, but use ElemType.class in the doIt method directly. Would that have the same effect? (It would syntactically be okay.) If so, that is exactly what I was looking for (even though I will not use it, for the reasons Jan Gräfen outlined, and which were obvious all the time...). – TheBlastOne Mar 13 '12 at 18:36
  • 1
    I updated my response to include a solution that would work even without the second parameter :) – Cagil Seker Mar 13 '12 at 18:48
  • Yeah that's cool. Thanks for throwing this at me, that was what I was looking for. – TheBlastOne Mar 13 '12 at 23:38
  • The hacky stuff does not work: "java.lang.ClassCastException: sun.reflect.generics.reflectiveObjects.TypeVariableImpl incompatible with java.lang.Class". The Class parameter stuff works (of course). – TheBlastOne Mar 14 '12 at 08:00
  • hmmm.. That "getGenericSuperclass()" part had compiled and run on my machine. But I understand there maybe some limitations to its usage. I am at work now, but I'll take a look at it after I get to home. – Cagil Seker Mar 14 '12 at 12:25
  • In fact you are right, I have overlooked an empty catch() statement previously. I tried many more techniques and come to the conclusion that it is not possible without changing some classes as mentioned. But I got an idea with the clone method (if provided) to get a new DerivedType object and explained it above. Please see my updated answer. It seems to work on my test cases... – Cagil Seker Mar 14 '12 at 19:58
  • OK, thanks. >>In short, Java runtime system does not store the parameterized types (in favor of backwards compatibility; so this may change in the future).<< Isn't this simply because there is no type info available due to type erasure (again)? If we get the ArrayList, but no element, there is no type info about the element at runtime, only about the ArrayList (excluding its elements, if any). This is where I would give up and let the caller pass a class parameter argument. The polymorphism trick is neat, but the ArrayList can be empty initially, so the D o = target.get(0); call will fail. – TheBlastOne Mar 15 '12 at 07:32
  • The array option is not really an option since ArrayLists are the targets to fill with new instances. But how crazy is that array idea...come on!!! :D – TheBlastOne Mar 15 '12 at 07:35
  • 1
    I think I've learned a lot. You don't have to invest more time. I will live with the class parameter for now, and for new designs, I'd stick to the factory suggestion. It is, however, astounding that while most people throw generic methods and generic types into one bucket, there seem to be subtle differences when it comes to using reflection on/in them -- so you really got the point and were very helpful, thank you for that. – TheBlastOne Mar 15 '12 at 07:38
2

You can't create an ElemType object here since the compiler can't know exactly what an ElemType will be once the generic code is instantiated.

To allow creation of an ElemType, I would supply a factory object of some sort. You could use a Java reflection class, but it's probably easier to supply your own factory base class or interface.

njlarsson
  • 2,128
  • 1
  • 18
  • 27
  • I feared so. But how would I use "a Java reflection class"? I don´t see how I can fetch reflection info in doIt. How could I call newInstance, or getConstructor? If I could, I would be fine. – TheBlastOne Mar 13 '12 at 18:06
  • Ah. I see. There is nothing to reflect upon. There is no generic-typed parameter of type , just an ArrayList, and that will be a raw ArrayList due to type erasure. – TheBlastOne Mar 13 '12 at 18:14
  • @TheBlastOne: You must add a parameter of type `java.lang.Class` or just a class name, then you can use reflection, everything else does not work due to type ereasure. – home Mar 13 '12 at 18:16
0

Imagine that you have this:

public class DerivedType extends BaseClass
{
    // No more one argument constructor
    public DerivedType() {
        super(0);
    }
}

Then the call

DerivedType d = new DerivedType(5);

is not valid...

assylias
  • 321,522
  • 82
  • 660
  • 783