Mehmet Ataş
Mehmet Ataş

Reputation: 11559

Java: Create an object whose type is a type parameter

I want to write the equivalent Java code of a C# code.

My C# code is as follows:

public abstract class A<T> where T : A<T>, new()
{
    public static void Process()
    {
        Process(new T());
    }

    public static void Process(T t)
    {
        // Do Something...
    }
}

public class B : A<B>
{
}

public class C : A<C>
{
}

Java equivalent of my code looks like this.

public abstract class A<T extends A<T>>
{
    public static <T extends A<T>> void process()
    {
        process(new T()); // Error: Cannot instantiate the type T 
    }

    public static <T extends A<T>> void process(T t)
    {
        // Do Something...
    }

    public class B extends A<B>
    {
    }

    public class C extends A<C>
    {
    }
}

Here the "new()" syntax in class declaration forces derived classes to write a default constructer which makes possible to call "new T()" from base class. In other words when i am wrting the base class i am sure that the derived class will have a default constructer, so that i can instantiate a derived class object from base class.

My problem in Java is, I cannot instantiate a derived class object from super class. I get "Cannot instantiate the type T" error for "new T()" call. Is there any C# similar way in Java or should I use something like prototype pattern and cloning?

Upvotes: 7

Views: 977

Answers (6)

irreputable
irreputable

Reputation: 45443

Actually this is not a problem in Java. The idiom is passing the class

    public static <T extends A<T>> T process(Class<T> clazz) 
    {
        T o = clazz.newInstance();
        process( o ); 
        return o;
    }

    X x = process(X.class); // not too verbose

I added a return value to illustrate the general case.

Upvotes: 0

RalphChapin
RalphChapin

Reputation: 3158

In addition to the other comments, I would suggest not using generics. They are not needed--they get stripped out at compile time anyway--and if you do not know them well you will try to make them do things they cannot.

Once you have your class working properly, then add them back in. Your IDE will, at that point, give you a lot of useful and intelligible advice, and the generics will warn you when you use objects of the wrong class.

It does look possible to me that this class will not need generics at all when finished. (I don't know what else this class may do, and I do not understand the use of the static methods--they will never have access to an individual instance's type information.)

Upvotes: 0

Tom Hawtin - tackline
Tom Hawtin - tackline

Reputation: 147154

Just use the bog standard Abstract Factory pattern. You then get the additional benefits that you are not tying down to a specific type, the implementation type need not have a specific constructor, the instance can have some parameterisation, instances can be cached, etc., etc.

For the love of god, don't use reflection.

Upvotes: 0

Andrey Taptunov
Andrey Taptunov

Reputation: 9505

You can find some explanation of the difference between generics in C# and Java from this li nk - comparing java and C# generics.

Java generics are a completely compile-time construct. You cannot do anything with generic type parameters that rely in any way on runtime information. This includes:

  • Creating instances of generic type parameters.
  • Creating arrays of generic type parameters.
  • Quering the runtime class of a generic type parameter.
  • Using instanceof with generic type parameters.

You can bypass this restriction with java.lang.reflect namepsace. For example see this stackoverflow question: Genercs and Class.forName()

Upvotes: 2

ncmathsadist
ncmathsadist

Reputation: 4889

Also, beware of this if you are using generics.

T[] someArray = new T[];

This is one reason to prefer ArrayList to arrays. The reason for the problem lies with reifiability and type erasure.

Upvotes: 0

Lawrence McAlpin
Lawrence McAlpin

Reputation: 2785

Java doesn't support reified generics, so there is no equivalent to "new T();". The way I work around this is to use reflection against a type token. The type token indicates what the generic type is.

public abstract class A<T> {
  private Class<T> typeToken;
  // constructor
  public A() {
        typeToken = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
  }
}

Then use reflection to instantiate the class. It's ugly, but it gets the job done.

Upvotes: 5

Related Questions