0

Say, for example, that there is a block of code as follows:

public className<T> {
    public className(T[] varName) {
        //somecode
    }
}

How would I get the type of T from an instance of T[], and then use it to instantiate an object of type T?

Redman
  • 3
  • 1
  • I'm afraid you cannot do this. Generics are erased at runtime, so your method will get an `Object[] `, not a `T[]`. You'd have to pass a class object, see [Instantiating generics type in java](https://stackoverflow.com/questions/2434041/instantiating-generics-type-in-java) – TiiJ7 Sep 06 '18 at 07:06
  • the answer is cannot, but C# can do. You have to pass the class as param – yelliver Sep 06 '18 at 07:24

1 Answers1

0

In order for you to be able to instantiate any element of type T in this situation you will need to use reflection and also some conditions have to be met:

  • you can only get the type of T if the array is not null and there is at least one element in the array. Due to type erasure there is no direct way of inferring the type without the presence of an actual object in the array.

  • then you have to check if the constructor of the first object in the array has a no args argument.

Here is a executable example below which you can try out:

import javax.swing.*;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;

public class TypeClass<T> {

    public TypeClass(T[] array) throws IllegalAccessException, InvocationTargetException, InstantiationException {
        if(array != null && array.length > 0) { // You can only get the type of T if the arrays is not null and there is at least one element in the array
            T first = array[0];
            Class<?> clazz = first.getClass();
            Constructor<?>[] constructors = clazz.getConstructors();
            for(Constructor<?> constructor : constructors) {
                int parameterCount = constructor.getParameterCount(); // Check if the constructor has a no args argument
                if(parameterCount == 0) {
                    T o = (T) constructor.newInstance();
                    // Do something with the new object
                    System.out.println(o);
                }
            }
        }
    }

    public static void main(String[] args) throws IllegalAccessException, InstantiationException, InvocationTargetException {
        JFrame[] jframes = new JFrame[]{new JFrame()};
        TypeClass<JFrame> typeClass = new TypeClass<>(jframes);
    }
}

If you run this you will see something like this om the console:

javax.swing.JFrame[frame0,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=,resizable,normal,defaultCloseOperation=HIDE_ON_CLOSE,rootPane=javax.swing.JRootPane[,0,0,0x0,invalid,layout=javax.swing.JRootPane$RootLayout,alignmentX=0.0,alignmentY=0.0,border=,flags=16777673,maximumSize=,minimumSize=,preferredSize=],rootPaneCheckingEnabled=true]

Update

You could also specify an alternative constructor which indicates specifically the type to instantiate via reflection. This makes more sense, because the type of elements in the array might actually subclass type T. Here is an example of an alternative constructor:

// Alternative with the specific type for instantiation
public TypeClass(T[] array, Class<T> tClass) throws IllegalAccessException, InvocationTargetException, InstantiationException {
    if(tClass != null) {
        Class<?> clazz = tClass.getClass();
        Constructor<?>[] constructors = clazz.getConstructors();
        for(Constructor<?> constructor : constructors) {
            int parameterCount = constructor.getParameterCount(); // Check if the constructor has a no args argument
            if(parameterCount == 0) {
                T o = (T) constructor.newInstance();
                // Do something with the new object
                System.out.println(o);
            }
        }
    }
}
gil.fernandes
  • 9,585
  • 3
  • 41
  • 57
  • I am not sure if this the way to go. An array `MyObject[]` can contain elements of subclasses of `MyObject`. Using just the first element seems to me quite arbitrary. Creating an arbitrary instance may have unpredictable consequences. – LuCio Sep 06 '18 at 08:11
  • @LuCio you are right. There might be subclasses. Another option which would be better would be to change the Constructor with `Class clazz` and specify exactly the type you want to instantiate. That is for sure a better solution than this. – gil.fernandes Sep 06 '18 at 10:37
  • In the new method the `array` is unused, so it doesn't answer actually the question. If it is just to "_get the type of `T` from an instance of `T[]`_", we can call `array.getClass().getComponentType()`. You also gave _an_ answer and it might be an appropriate answer. But IMO the OP missed to clarify the context yet. Therefore my first comment needs to be answered by the OP. But now the game is over ;) – LuCio Sep 06 '18 at 11:29