jabal
jabal

Reputation: 12347

Java generics: instantiating A with a single constructor argument B

I'm looking for a nice pattern to do the following: I have a class FooBar<A,B> and I know that A has a constructor with a single B parameter. I'd like to have a protected method in FooBar with the following signature:

protected A chuckNorris42(B b); // intent: return new A(b)

Of course the above mentioned "intent" is not compilable. As a workaround I made this method abstract and at the point where I instantiate FooBar with concrete classes I implemented chuckNorris42 inline:

FooBar<Chuck, Norris> = new FooBar<Chuck, Norris>() {
  protected Chuck chuckNorris42(Norris norris) {
    return new Chuck(norris);  
  }
}

It works, but I wondered if there is a nicer or cleaner way to achieve the same thing so I decided to pose the question here.

Upvotes: 2

Views: 452

Answers (3)

Philipp Wendler
Philipp Wendler

Reputation: 11433

The point is, you don't know that any actual instance of A has such a constructor, the Java type system just is not strong enough to enforce this. Thus there is no clean generic built-in way to call the constructor of a class which you don't know statically. You have to use reflection like in the other answers, but this might of course fail at runtime if A suddenly is instantiated with a type which is either not instantiable (A may be an abstract class, for example!), or does not have the appropriate constructor. If you want to avoid this, you could use factories:

interface Factory<A, B> {
  A createInstance(B b);
}

Then let the user of your class FooBar give you an instance of that factory for the current values of types A and B.

Upvotes: 4

default locale
default locale

Reputation: 13446

I'm not sure how much it's nicer and cleaner but you can try something like that:

public class FooBar<A,B> {
private Class<A> aClass;
public FooBar(Class<A> aClass) {
    this.aClass = aClass;
}

protected A chuckNorris42(B b) throws NoSuchMethodException, InstantiationException,IllegalAccessException,InvocationTargetException{
    return aClass.getConstructor(b.getClass()).newInstance(b);
}
}

There're no way to get class of a generic argument. So, one of the methods to instantiate object of generic argument class is to pass class in constructor.

UPDATE: Also there is a way to split generic arguments down the hierarchy:

public abstract class Foobar<A,B> {
   protected abstract A chuckNorris(B b);
}

public class FooBarChuck<B> extends Foobar<Chuck<B>,B>{

   @Override
  protected Chuck<B> chuckNorris(B b) {
      return new Chuck<B>(b);
  }
}
public class Chuck<B> {
  public Chuck(B b) {
    //something here
  }
}

Then you don't have to reimplement this method for all B classes but you still have to create separate class for each Chuck.

P.S. I actually don't think that this methods are much cleaner than yours :)

Upvotes: 2

max.shmidov
max.shmidov

Reputation: 131

You may try reflection, something like

public A produce(Class<A> clazz, B arg) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException {
    return clazz.getConstructor(arg.getClass()).newInstance(arg);
}

Upvotes: 0

Related Questions