2

I have a class

class Holder<SomeType extends AbstractType>{
    SomeType createType(){...}
}

then outside of it, I call

Holder holder = new Holder<MyType>();

I can't figure out how to implement the createType() method.

I tried about everything I could find in StackOverflow and the web, but I always run in the same dead end: At runtime, if I do

TypeVariable typeVariable = getClass().getTypeParameters()[0];
System.err.println("typeVariable "+typeVariable);

I get "SomeType" instead of "MyType", and so I can't fetch the appropriate Constructor since I want to use Class.forName("MyType") and not Class.forName("SomeType") which lead to a java.lang.ClassNotFoundException

I use SomeType extends AbstractType, so I known all my MyTypes will have the same constructor available.

I can't use

ParameterizedType t = (ParameterizedType) MyClass.class.getGenericSuperclass();

since Holder is not a Subclass of anything useful

I could do something like this in my Holder class, but I'm looking for a way to use reflection without having to pass an argument in the Constructor

private final Class<SomeType> type;

public Holder(Class<SomeType> type) {
    this.type = type;
}

Trying all the various ideas I saw on the net feels like I'm chasing my tail, so I guess I'm missing an obvious element here.

Thanks for your help!

EDIT : i can't solve it like in Create instance of generic type in Java? I can't pass the class as an argument of createType(Class) because I will not known the class at the moment of the call to createType (this method supposed to create the child of appropriate type when i can't known which type my children are supposed to be)

Community
  • 1
  • 1
  • 2
    Possible duplicate of [Create instance of generic type in Java?](http://stackoverflow.com/questions/75175/create-instance-of-generic-type-in-java) – OH GOD SPIDERS Mar 21 '17 at 10:45
  • Due to type erasure the generic type of the call `new Holder()` isn't known at runtime so all you can do is pass that information somehow, e.g. by passing in the type class. Alternatively you could create subclasses e.g. `class MyTypeHolder extends Holder` in which case you could [use reflection to extract the generic type](http://www.artima.com/weblogs/viewpost.jsp?thread=208860). – Thomas Mar 21 '17 at 10:45
  • Perhaps you can change the declaration of createType to T createType(Class extends AbstractType> clz) {} and then call it like SomeType sType = (SomeType)holder.createType(MyType.class); This way the class Holder doesn't have to be generic. – dsp_user Mar 21 '17 at 10:54
  • Are you referring to `.class.newInstance()` perhaps? – Nathangrad Mar 21 '17 at 10:56
  • Thanks Thomas, i tried my approach to get rid of the 20+ MyType1Holder, MyType2Holder... I guess I'll just have to pass the class in the constructor of Holder to avoid type erasure. I you'd reply instead of comment, i could upvote your answer ;) – Thomas Ludwig Mar 21 '17 at 11:08
  • What is the signature of the constructor you want to use? And is `createType` the only method? – Jorn Vernee Mar 21 '17 at 11:09
  • the signature the MyType(String someParameter) and no Holder has many methods. createType just creates a new child to be stores in a List – Thomas Ludwig Mar 21 '17 at 11:12

1 Answers1

0

Like Thomas said in the comments, Java uses type erasure. That means that the concrete type that fills in SomeType is not implicitly passed to the Holder. But to instantiate a class, you need that type information. So you have to explicitly pass that information to Holder. There are several ways of doing this, one of which you have found. Which is passing a Class object to Holder when constructing it.

Java 8 introduces method references that also allow you to reference a constructor:

Function<String, MyType> cons = MyType::new;

This will create an instance of the functional interface Function that has a method apply that takes a String and returns a MyType.

You could then pass this functor to the constructor of Holder, and save it to use in createType:

class Holder<SomeType extends AbstractType>{
    private final Function<String, SomeType> cons;

    public Holder(Function<String, SomeType> cons) {
        this.cons = cons;
        ...
    }

    SomeType createType() {
        return cons.apply("SomeString");
    }
}

The advantage of this over using a Class in the same way, is that you know the signature of the constructor at compile time, and there are no exceptions thrown.

Graham
  • 7,431
  • 18
  • 59
  • 84
Jorn Vernee
  • 31,735
  • 4
  • 76
  • 93