how to instantiate new return object of generic type

Discussion in 'Java' started by tom forsmo, Apr 16, 2007.

  1. tom forsmo

    tom forsmo Guest

    Hi

    I have a problem with generics I cant seem to solve.

    Consider this example pseudo-code:

    public class Table<K, V> {

    public class Elem<V> {

    String key;
    V value;

    public getValue();
    }

    public V getElem(key, locale) {

    Elem e = findElem(key);

    V val = e.getValue(key);

    return ( val )
    }
    }

    I want to add to some code to getElem() (not findElem()) that creates a
    completely new val instance/value of type V under certain circumstances.
    The problem is, how do I do this when I dont know the actual type of V is?

    (For bizarre reasons this is the only way to do it, I think. The
    underlying functionality of findElem() can not be extended/changed to do
    what I want, because that would require an entire project of its own.
    But if anybody has any other suggestion on how to solve the problem
    without starting a new subproject, I'd be happy to hear it.)

    regards

    tom
    tom forsmo, Apr 16, 2007
    #1
    1. Advertising

  2. On 16.04.2007 14:49, tom forsmo wrote:
    > Hi
    >
    > I have a problem with generics I cant seem to solve.
    >
    > Consider this example pseudo-code:
    >
    > public class Table<K, V> {
    >
    > public class Elem<V> {
    >
    > String key;
    > V value;
    >
    > public getValue();
    > }
    >
    > public V getElem(key, locale) {
    >
    > Elem e = findElem(key);
    >
    > V val = e.getValue(key);
    >
    > return ( val )
    > }
    > }
    >
    > I want to add to some code to getElem() (not findElem()) that creates a
    > completely new val instance/value of type V under certain circumstances.
    > The problem is, how do I do this when I dont know the actual type of V is?


    You can either use reflection or you define a generic interface for a
    factory object that will create a new object from the given instance.
    Something along the lines of

    interface Factory<V> {
    V create(V template);
    }

    Then your Table needs a member with this type to which the creation is
    delegated. A generic default implementation would probably just return
    the argument.

    I'd use the factory approach as it is more flexible and simpler.

    > (For bizarre reasons this is the only way to do it, I think. The
    > underlying functionality of findElem() can not be extended/changed to do
    > what I want, because that would require an entire project of its own.
    > But if anybody has any other suggestion on how to solve the problem
    > without starting a new subproject, I'd be happy to hear it.)


    Kind regards

    robert
    Robert Klemme, Apr 16, 2007
    #2
    1. Advertising

  3. tom forsmo

    tom forsmo Guest

    Robert Klemme wrote:
    > On 16.04.2007 14:49, tom forsmo wrote:
    >
    > You can either use reflection or you define a generic interface for a
    > factory object that will create a new object from the given instance.
    > Something along the lines of
    >
    > interface Factory<V> {
    > V create(V template);
    > }


    Ok, so how do I do actually create the new object and how do I find out
    what type of object I am to create?

    creation example:

    V val = new String(message);

    is not legal, nor is casting it to V since V is unknown.

    tom
    tom forsmo, Apr 17, 2007
    #3
  4. tom forsmo

    Daniel Pitts Guest

    On Apr 16, 11:29 pm, tom forsmo <> wrote:
    > Robert Klemme wrote:
    > > On 16.04.2007 14:49, tom forsmo wrote:

    >
    > > You can either use reflection or you define a generic interface for a
    > > factory object that will create a new object from the given instance.
    > > Something along the lines of

    >
    > > interface Factory<V> {
    > > V create(V template);
    > > }

    >
    > Ok, so how do I do actually create the new object and how do I find out
    > what type of object I am to create?
    >
    > creation example:
    >
    > V val = new String(message);
    >
    > is not legal, nor is casting it to V since V is unknown.
    >
    > tom


    You have to create a explicit Factory for the types you want to
    create.
    interface Factory<V> {
    V create(V template);
    }

    class Table<K, V> {
    private final Factory<V> factory;
    public Table(Factory<V> factory) {
    this.factory = factory;
    }

    public V get(K key, Locale locale) {
    return factory.create(find(key));
    }
    }

    new Table<String, Number>(
    new Factory<Number>() {
    Number create(Number template) {
    return template == null ? Integer.valueOf(0) :
    Integer.valueOf(template.intValue());
    }
    }
    ).get("something");
    Daniel Pitts, Apr 17, 2007
    #4
  5. tom forsmo

    tom forsmo Guest

    Daniel Pitts wrote:
    >
    > You have to create a explicit Factory for the types you want to
    > create.
    >
    > new Table<String, Number>(
    > new Factory<Number>() {
    > Number create(Number template) {
    > return template == null ? Integer.valueOf(0) :
    > Integer.valueOf(template.intValue());
    > }
    > }
    > ).get("something");


    Ok, I see but unfortunately thats not going to work, because it depends
    on the client controlling the instantion of the Table its values. In
    this case its Hibernate that controls this, by reflection and such.
    Hibernate spits out a table of elements with values for which I have no
    idea what kind of type they are, and I have to modify the returned value
    if need be, without knowing the type.

    (arrgh... If anybody wants a truck load of java consultants from
    BigIntlConsultingCo, you can have them for free!!! Just grab them and
    bag them and put them in your local zoo, out of harms way... Or send
    them to a zoo in Siberia, if you prefer.)

    I'll be more specific, the underlying system stores value messages in a
    database controlled by hibernate. I am working on an architectural
    framework that does not control the specifics of the diverse
    implementations. the value messages are by default in our local
    langauge. And somebody forgot to consider that these messages must be
    available in other langauges as well. Since things are being used by
    many projects already, a rewrite of the original code is out of the
    question. So my solution is to add the locale functionality in the
    outermost code, so we dont have to change the underlying code. But as
    you might have gathered by now, since everything is in a generic type I
    can not instantiate a correct return object and pass it through the
    generic return type. This is where the problem lies.

    Suggestions are wholeheartedly welcomed

    tom
    tom forsmo, Apr 17, 2007
    #5
  6. -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    tom forsmo schreef:
    > Daniel Pitts wrote:
    >>
    >> You have to create a explicit Factory for the types you want to
    >> create.
    >>
    >> new Table<String, Number>(
    >> new Factory<Number>() {
    >> Number create(Number template) {
    >> return template == null ? Integer.valueOf(0) :
    >> Integer.valueOf(template.intValue());
    >> }
    >> }
    >> ).get("something");

    >
    > Ok, I see but unfortunately thats not going to work, because it depends
    > on the client controlling the instantion of the Table its values. In
    > this case its Hibernate that controls this, by reflection and such.
    > Hibernate spits out a table of elements with values for which I have no
    > idea what kind of type they are, and I have to modify the returned value
    > if need be, without knowing the type.
    >
    > (arrgh... If anybody wants a truck load of java consultants from
    > BigIntlConsultingCo, you can have them for free!!! Just grab them and
    > bag them and put them in your local zoo, out of harms way... Or send
    > them to a zoo in Siberia, if you prefer.)
    >
    > I'll be more specific, the underlying system stores value messages in a
    > database controlled by hibernate. I am working on an architectural
    > framework that does not control the specifics of the diverse
    > implementations. the value messages are by default in our local
    > langauge. And somebody forgot to consider that these messages must be
    > available in other langauges as well. Since things are being used by
    > many projects already, a rewrite of the original code is out of the
    > question. So my solution is to add the locale functionality in the
    > outermost code, so we dont have to change the underlying code. But as
    > you might have gathered by now, since everything is in a generic type I
    > can not instantiate a correct return object and pass it through the
    > generic return type. This is where the problem lies.
    >
    > Suggestions are wholeheartedly welcomed


    I think you might get inspired by browsing through Jakarta Commons
    Collections, specifically the Functors package and how it is used in
    maps, e.g. DefaultedMap or LazyMap. Unfortunately, they aren’t generic.
    I have generified most of it for my own use and would be happy to share
    it here. Hm, maybe I should put that on my website. There is a
    sourceforge project that has generified JCC, but not as stringent as I’d
    like it.

    H.

    - --
    Hendrik Maryns
    http://tcl.sfs.uni-tuebingen.de/~hendrik/
    ==================
    http://aouw.org
    Ask smart questions, get good answers:
    http://www.catb.org/~esr/faqs/smart-questions.html
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.5 (GNU/Linux)

    iD8DBQFGJJphe+7xMGD3itQRAnN+AJ0TIDg7bjK1NbAfBichcIOO2wO7pQCfeADp
    AwUJe+gN/yi3jzW+j8g64bI=
    =KDmG
    -----END PGP SIGNATURE-----
    Hendrik Maryns, Apr 17, 2007
    #6
  7. On 17.04.2007 10:16, tom forsmo wrote:
    > I'll be more specific, the underlying system stores value messages in a
    > database controlled by hibernate. I am working on an architectural
    > framework that does not control the specifics of the diverse
    > implementations. the value messages are by default in our local
    > langauge. And somebody forgot to consider that these messages must be
    > available in other langauges as well. Since things are being used by
    > many projects already, a rewrite of the original code is out of the
    > question. So my solution is to add the locale functionality in the
    > outermost code, so we dont have to change the underlying code. But as
    > you might have gathered by now, since everything is in a generic type I
    > can not instantiate a correct return object and pass it through the
    > generic return type. This is where the problem lies.


    The complete architecture is not fully clear to me, but maybe your
    framework is too generic.

    The only other option that comes to mind at the moment is to try to
    clone the object or serialize and deserialize and use whatever succeeds.

    robert
    Robert Klemme, Apr 17, 2007
    #7
  8. -----BEGIN PGP SIGNED MESSAGE-----
    Hash: SHA1

    Robert Klemme schreef:
    > On 17.04.2007 10:16, tom forsmo wrote:
    >> I'll be more specific, the underlying system stores value messages in
    >> a database controlled by hibernate. I am working on an architectural
    >> framework that does not control the specifics of the diverse
    >> implementations. the value messages are by default in our local
    >> langauge. And somebody forgot to consider that these messages must be
    >> available in other langauges as well. Since things are being used by
    >> many projects already, a rewrite of the original code is out of the
    >> question. So my solution is to add the locale functionality in the
    >> outermost code, so we dont have to change the underlying code. But as
    >> you might have gathered by now, since everything is in a generic type
    >> I can not instantiate a correct return object and pass it through the
    >> generic return type. This is where the problem lies.

    >
    > The complete architecture is not fully clear to me, but maybe your
    > framework is too generic.
    >
    > The only other option that comes to mind at the moment is to try to
    > clone the object or serialize and deserialize and use whatever succeeds.


    Hm, here are some snippets from my generified JCC, they might give you
    some ideas. You’ll notice that @SuppressWarnings("unchecked") is
    used quite often. It is inevitable. For Javadoc, see the Jakarta website.

    public interface Factory<T> {

    public T create();

    }

    public class ConstantFactory<T> implements Factory<T>, Serializable {

    /** Returns null each time */
    @SuppressWarnings("unchecked")
    public static final Factory NULL_INSTANCE = new ConstantFactory(null);

    private final T iConstant;

    @SuppressWarnings("unchecked")
    public static <T> Factory<T> getInstance(T constantToReturn) {
    if (constantToReturn == null) {
    return NULL_INSTANCE;
    }
    return new ConstantFactory<T>(constantToReturn);
    }

    public ConstantFactory(T constantToReturn) {
    super();
    iConstant = constantToReturn;
    }

    /**
    * Always return constant.
    *
    * @return the stored constant value
    */
    public T create() {
    return iConstant;
    }

    }

    public class InstantiateFactory<T> implements Factory<T>, Serializable {

    /** The class to create */
    private final Class<? extends T> iClassToInstantiate;
    /** The constructor parameter types */
    private final Class<?>[] iParamTypes;
    /** The constructor arguments */
    private final Object[] iArgs;
    /** The constructor */
    private transient Constructor<? extends T> iConstructor = null;

    /**
    * Factory method that performs validation.
    *
    * @param classToInstantiate the class to instantiate, not null
    * @param paramTypes the constructor parameter types
    * @param args the constructor arguments
    * @return a new instantiate factory
    */
    public static <T> Factory<T> getInstance(Class<T>
    classToInstantiate, Class<?>[] paramTypes, Object[] args) {
    if (classToInstantiate == null) {
    throw new IllegalArgumentException("Class to instantiate
    must not be null");
    }
    if (((paramTypes == null) && (args != null))
    || ((paramTypes != null) && (args == null))
    || ((paramTypes != null) && (args != null) &&
    (paramTypes.length != args.length))) {
    throw new IllegalArgumentException("Parameter types must
    match the arguments");
    }

    if (paramTypes == null || paramTypes.length == 0) {
    return new InstantiateFactory<T>(classToInstantiate);
    } else {
    paramTypes = paramTypes.clone();
    args = args.clone();
    return new InstantiateFactory<T>(classToInstantiate,
    paramTypes, args);
    }
    }

    /**
    * Constructor that performs no validation.
    * Use <code>getInstance</code> if you want that.
    *
    * @param classToInstantiate the class to instantiate
    */
    public InstantiateFactory(Class<? extends T> classToInstantiate) {
    super();
    iClassToInstantiate = classToInstantiate;
    iParamTypes = null;
    iArgs = null;
    findConstructor();
    }

    /**
    * Constructor that performs no validation.
    * Use <code>getInstance</code> if you want that.
    *
    * @param classToInstantiate the class to instantiate
    * @param paramTypes the constructor parameter types, not cloned
    * @param args the constructor arguments, not cloned
    */
    public InstantiateFactory(Class<? extends T> classToInstantiate,
    Class<?>[] paramTypes, Object[] args) {
    super();
    iClassToInstantiate = classToInstantiate;
    iParamTypes = paramTypes;
    iArgs = args;
    findConstructor();
    }

    /**
    * Find the Constructor for the class specified.
    */
    private void findConstructor() {
    try {
    iConstructor = iClassToInstantiate.getConstructor(iParamTypes);

    } catch (NoSuchMethodException ex) {
    throw new IllegalArgumentException("InstantiateFactory: The
    constructor must exist and be public ");
    }
    }

    public T create() {
    // needed for post-serialization
    if (iConstructor == null) {
    findConstructor();
    }

    try {
    return iConstructor.newInstance(iArgs);

    } catch (InstantiationException ex) {
    throw new FunctorException("InstantiateFactory:
    InstantiationException", ex);
    } catch (IllegalAccessException ex) {
    throw new FunctorException("InstantiateFactory: Constructor
    must be public", ex);
    } catch (InvocationTargetException ex) {
    throw new FunctorException("InstantiateFactory: Constructor
    threw an exception", ex);
    }
    }

    }

    ==> I guess this is probably what you want.

    A more complicated approach (I got a lot of problems getting this to
    compile, it does in Eclipse):

    public class PrototypeFactory {

    @SuppressWarnings("unchecked")
    public static <T> Factory<T> getInstance(T prototype) {
    if (prototype == null) {
    return ConstantFactory.getInstance(null);
    }
    try {
    Method method = prototype.getClass().getMethod("clone",
    (Class[]) null);
    return new PrototypeCloneFactory<T>(prototype, method);

    } catch (NoSuchMethodException ex) {
    try {
    prototype.getClass().getConstructor(new Class[] {
    prototype.getClass()});
    return new InstantiateFactory<T>(
    (Class<T>) prototype.getClass(), // this used to
    compile without the cast??
    new Class[] { prototype.getClass()},
    new Object[] { prototype });

    } catch (NoSuchMethodException ex2) {
    if (prototype instanceof Serializable) {
    return (Factory<T>) new
    PrototypeSerializationFactory<Serializable>((Serializable)prototype);
    }
    }
    }
    throw new IllegalArgumentException("The prototype must be
    cloneable via a public clone method");
    }

    private PrototypeFactory() {
    super();
    }

    // PrototypeCloneFactory

    //-----------------------------------------------------------------------
    /**
    * PrototypeCloneFactory creates objects by copying a prototype
    using the clone method.
    */
    static class PrototypeCloneFactory<T> implements Factory<T>,
    Serializable {

    /** The object to clone each time */
    private final Object iPrototype;
    /** The method used to clone */
    private transient Method iCloneMethod;

    private PrototypeCloneFactory(Object prototype, Method method) {
    super();
    iPrototype = prototype;
    iCloneMethod = method;
    }

    /**
    * Find the Clone method for the class specified.
    */
    private void findCloneMethod() {
    try {
    iCloneMethod = iPrototype.getClass().getMethod("clone",
    (Class[]) null);

    } catch (NoSuchMethodException ex) {
    throw new
    IllegalArgumentException("PrototypeCloneFactory: The clone method must
    exist and be public ");
    }
    }

    @SuppressWarnings("unchecked")
    public T create() {
    // needed for post-serialization
    if (iCloneMethod == null) {
    findCloneMethod();
    }

    try {
    return (T) iCloneMethod.invoke(iPrototype, (Object[])null);

    } catch (IllegalAccessException ex) {
    throw new FunctorException("PrototypeCloneFactory: Clone
    method must be public", ex);
    } catch (InvocationTargetException ex) {
    throw new FunctorException("PrototypeCloneFactory: Clone
    method threw an exception", ex);
    }
    }
    }

    // PrototypeSerializationFactory

    //-----------------------------------------------------------------------
    /**
    * PrototypeSerializationFactory creates objects by cloning a
    prototype using serialization.
    */
    static class PrototypeSerializationFactory<T extends Serializable>
    implements Factory<T>, Serializable {

    /** The object to clone via serialization each time */
    private final T iPrototype;

    private PrototypeSerializationFactory(T prototype) {
    super();
    iPrototype = prototype;
    }

    /**
    * Creates an object using serialization.
    *
    * @return the new object
    */
    @SuppressWarnings("unchecked")
    public T create() {
    ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
    ByteArrayInputStream bais = null;
    try {
    ObjectOutputStream out = new ObjectOutputStream(baos);
    out.writeObject(iPrototype);

    bais = new ByteArrayInputStream(baos.toByteArray());
    ObjectInputStream in = new ObjectInputStream(bais);
    return (T) in.readObject();

    } catch (ClassNotFoundException ex) {
    throw new FunctorException(ex);
    } catch (IOException ex) {
    throw new FunctorException(ex);
    } finally {
    try {
    if (bais != null) {
    bais.close();
    }
    } catch (IOException ex) {
    // ignore
    }
    try {
    if (baos != null) {
    baos.close();
    }
    } catch (IOException ex) {
    // ignore
    }
    }
    }
    }

    }

    This illustrates how they are used, e.g. in a List. Similar for
    LazySet, LazyMap etc. For Maps, a similar approach is used, but with
    Transformers instead of Factories, e.g. they get an input:
    public interface Transformer<I,O> {

    public O transform(I input);

    }


    public class LazyList<E> extends AbstractSerializableListDecorator<E> {

    protected final Factory<E> factory;

    public static <E> List<E> decorate(List<E> list, Factory<E> factory) {
    return new LazyList<E>(list, factory);
    }


    //-----------------------------------------------------------------------
    protected LazyList(List<E> list, Factory<E> factory) {
    super(list);
    if (factory == null) {
    throw new IllegalArgumentException("Factory must not be null");
    }
    this.factory = factory;
    }


    //-----------------------------------------------------------------------
    /**
    * Decorate the get method to perform the lazy behaviour.
    * <p>
    * If the requested index is greater than the current size, the list
    will
    * grow to the new size and a new object will be returned from the
    factory.
    * Indexes in-between the old size and the requested size are left
    with a
    * placeholder that is replaced with a factory object when requested.
    *
    * @param index the index to retrieve
    */
    @Override
    public E get(int index) {
    int size = getList().size();
    if (index < size) {
    // within bounds, get the object
    E object = getList().get(index);
    if (object == null) {
    // item is a place holder, create new one, set and return
    object = factory.create();
    getList().set(index, object);
    return object;
    } else {
    // good and ready to go
    return object;
    }
    } else {
    // we have to grow the list
    for (int i = size; i < index; i++) {
    getList().add(null);
    }
    // create our last object, set and return
    E object = factory.create();
    getList().add(object);
    return object;
    }
    }

    }

    I hope you get some inspiration from this.
    H.
    - --
    Hendrik Maryns
    http://tcl.sfs.uni-tuebingen.de/~hendrik/
    ==================
    http://aouw.org
    Ask smart questions, get good answers:
    http://www.catb.org/~esr/faqs/smart-questions.html
    -----BEGIN PGP SIGNATURE-----
    Version: GnuPG v1.4.5 (GNU/Linux)

    iD8DBQFGJNu2e+7xMGD3itQRAui3AJ9vkD+V07dvgmQu8Yx/1F0VjEG6WQCfQTL8
    Ii7wjQxkSC/pxatgNTmN5H0=
    =OkBB
    -----END PGP SIGNATURE-----
    Hendrik Maryns, Apr 17, 2007
    #8
  9. tom forsmo

    Daniel Pitts Guest

    On Apr 17, 1:16 am, tom forsmo <> wrote:
    > Daniel Pitts wrote:
    >
    > > You have to create a explicit Factory for the types you want to
    > > create.

    >
    > > new Table<String, Number>(
    > > new Factory<Number>() {
    > > Number create(Number template) {
    > > return template == null ? Integer.valueOf(0) :
    > > Integer.valueOf(template.intValue());
    > > }
    > > }
    > > ).get("something");

    >
    > Ok, I see but unfortunately thats not going to work, because it depends
    > on the client controlling the instantion of the Table its values. In
    > this case its Hibernate that controls this, by reflection and such.
    > Hibernate spits out a table of elements with values for which I have no
    > idea what kind of type they are, and I have to modify the returned value
    > if need be, without knowing the type.
    >
    > (arrgh... If anybody wants a truck load of java consultants from
    > BigIntlConsultingCo, you can have them for free!!! Just grab them and
    > bag them and put them in your local zoo, out of harms way... Or send
    > them to a zoo in Siberia, if you prefer.)
    >
    > I'll be more specific, the underlying system stores value messages in a
    > database controlled by hibernate. I am working on an architectural
    > framework that does not control the specifics of the diverse
    > implementations. the value messages are by default in our local
    > langauge. And somebody forgot to consider that these messages must be
    > available in other langauges as well. Since things are being used by
    > many projects already, a rewrite of the original code is out of the
    > question. So my solution is to add the locale functionality in the
    > outermost code, so we dont have to change the underlying code. But as
    > you might have gathered by now, since everything is in a generic type I
    > can not instantiate a correct return object and pass it through the
    > generic return type. This is where the problem lies.
    >
    > Suggestions are wholeheartedly welcomed
    >
    > tom


    Perhaps you should instead of adding a "get" method, create a
    "LocalizedTable" type, which delegates to Tablle.find, but it
    translates using the technique I've shown you.
    Daniel Pitts, Apr 18, 2007
    #9
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Fei Liu
    Replies:
    4
    Views:
    771
    Victor Bazarov
    Oct 26, 2007
  2. Sideswipe
    Replies:
    6
    Views:
    832
  3. RyanN
    Replies:
    5
    Views:
    336
    George Sakkis
    Nov 10, 2008
  4. minlearn
    Replies:
    2
    Views:
    453
    red floyd
    Mar 13, 2009
  5. X X
    Replies:
    4
    Views:
    349
    red floyd
    Jul 19, 2010
Loading...

Share This Page