how to instantiate new return object of generic type

T

tom forsmo

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
 
R

Robert Klemme

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
 
T

tom forsmo

Robert said:
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
 
D

Daniel Pitts

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");
 
T

tom forsmo

Daniel said:
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
 
H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

tom forsmo schreef:
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-----
 
R

Robert Klemme

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
 
H

Hendrik Maryns

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Robert Klemme schreef:
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-----
 
D

Daniel Pitts

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.
 

Ask a Question

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

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,012
Latest member
RoxanneDzm

Latest Threads

Top