Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Instantiating a generic class in Java [duplicate]

Tags:

java

generics

People also ask

Can you instantiate a generic class in Java?

Due to the above conflict, instantiating a generic array in java is not permitted.

How do you declare a generic class in Java?

To update the Box class to use generics, you create a generic type declaration by changing the code "public class Box" to "public class Box<T>". This introduces the type variable, T, that can be used anywhere inside the class. As you can see, all occurrences of Object are replaced by T.

How do you initialize a generic object?

If you want to initialize Generic object, you need to pass Class<T> object to Java which helps Java to create generic object at runtime by using Java Reflection.


One option is to pass in Bar.class (or whatever type you're interested in - any way of specifying the appropriate Class<T> reference) and keep that value as a field:

public class Test {
    public static void main(String[] args) throws IllegalAccessException,
            InstantiationException {
        Generic<Bar> x = new Generic<>(Bar.class);
        Bar y = x.buildOne();
    }
}

public class Generic<T> {
    private Class<T> clazz;

    public Generic(Class<T> clazz) {
        this.clazz = clazz;
    }

    public T buildOne() throws InstantiationException, IllegalAccessException {
        return clazz.newInstance();
    }
}

public class Bar {
    public Bar() {
        System.out.println("Constructing");
    }
}

Another option is to have a "factory" interface, and you pass a factory to the constructor of the generic class. That's more flexible, and you don't need to worry about the reflection exceptions.


And this is the Factory implementation, as Jon Skeet suggested:

interface Factory<T> {
    T factory();
}

class Araba {
    //static inner class for Factory<T> implementation
    public static class ArabaFactory implements Factory<Araba> {
        public Araba factory() {
            return new Araba();
        }
    }
    public String toString() { return "Abubeee"; }
}

class Generic<T> {
    private T var;

    Generic(Factory<T> fact) {
        System.out.println("Constructor with Factory<T> parameter");
        var = fact.factory();
    }
    Generic(T var) {
        System.out.println("Constructor with T parameter");
        this.var = var;
    }
    T get() { return var; }
}

public class Main {
    public static void main(String[] string) {
        Generic<Araba> gen = new Generic<Araba>(new Araba.ArabaFactory());
        System.out.print(gen.get());
    }
}

Output:

Constructor with Factory<T> parameter
Abubeee

Here's a rather contrived way to do it without explicitly using an constructor argument. You need to extend a parameterized abstract class.

public class Test {   
    public static void main(String [] args) throws Exception {
        Generic g = new Generic();
        g.initParameter();
    }
}

import java.lang.reflect.ParameterizedType;
public abstract class GenericAbstract<T extends Foo> {
    protected T parameter;

    @SuppressWarnings("unchecked")
    void initParameter() throws Exception, ClassNotFoundException, 
        InstantiationException {
        // Get the class name of this instance's type.
        ParameterizedType pt
            = (ParameterizedType) getClass().getGenericSuperclass();
        // You may need this split or not, use logging to check
        String parameterClassName
            = pt.getActualTypeArguments()[0].toString().split("\\s")[1];
        // Instantiate the Parameter and initialize it.
        parameter = (T) Class.forName(parameterClassName).newInstance();
    }
}

public class Generic extends GenericAbstract<Foo> {
}

public class Foo {
    public Foo() {
        System.out.println("Foo constructor...");
    }
}

I really need to instantiate a T in Foo using a parameter-less constructor

Simple answer is "you cant do that" java uses type erasure to implment generics which would prevent you from doing this.

How can one work around Java's limitation?

One way (there could be others) is to pass the object that you would pass the instance of T to the constructor of Foo<T>. Or you could have a method setBar(T theInstanceofT); to get your T instead of instantiating in the class it self.


For Java 8 ....

There is a good solution at https://stackoverflow.com/a/36315051/2648077 post.

This uses Java 8 Supplier functional interface