How to instantiate an instance of a general method parameter in Java?

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 do I instantiate a type ElemType

in doIt

?

The shown construction gives the indicated error.

ElemType.newInstance

does not exist, which surprises me.

I've read pretty much all of the FAQs, answers and helpful stuff, but I can't find anything helpful.

EDIT: Yes, I know reflection has its drawbacks and is not a definitive solution for many reasons. The question is not "should I do this", but "how would I do it".

+2


source to share


4 answers


As mentioned, type erasure does not allow this. But you can achieve what you want:

public class BaseClass {

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

public class DerivedType extends BaseClass {
}

      

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

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 have to 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 really might want to be hacked 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 ;
    }
}

      



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

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

However, apart from the methods mentioned, I can think of two more things . First, if both BaseClass and DerivedType 'D' both implement the clone () method, you can get a clone of the 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 option is not a real "solution", but can be used if all you need is a new instance for an array of type-parameterized objects. The Erasure type is only used for parameterized types, but this is not the case for underlying arrays (arrays are overridden in the JVM). Therefore, if we have the freedom to change the method signature and work with arrays in order, then the following will 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. Supertext icons won't work for this problem if we can't enter new parameters. Please correct me if I am wrong.

+3


source


Please note that general information is erased by the compiler at compile time and replaced by an object. Internal generics are simply discarded from and to java.lang.Object.

This is also why it is difficult to get general information at runtime, even if possible.

See here: Google .

On a personal note: If you have to do something, it's usually bad design. I've been in this situation a couple of times, but each time I found a better solution :). So just consider if you really need such a dirty hack in your code.

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

Reflection should generally be used with caution anyway, as it is considered bad design from a software development perspective. What for? This can make it difficult to find bugs because reflection alters the natural flow of your application and uses information that is not always visible to you during development. It screams unexpected behavior.



And while I have no formal evidence for this, I would argue that every time you need some thought, there is another solution to your problem (if generative software is an option;)).

So, in the end, in 99% of all cases, reflection is nothing more than a dirty hack of a lazy program. This may be due to the fact that 100% of all programmers 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 )

+3


source


You cannot create an ElemType object here because the compiler cannot know exactly what the ElemType element will be after generating the generic code.

To create an ElemType, I would suggest a factory object. You can use a Java reflection class, but it is probably easier to provide your own factory base class or interface.

+2


source


Imagine you have this:

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

      

Then call

DerivedType d = new DerivedType(5);

      

not valid...

0


source







All Articles