I may have designed myself into a corner with this problem but I feel like there's a workable known solution to something like this that I'm not seeing. It may well be that I'm completely overcomplicating the problem and skipped over the obvious solution. Any advice would be greatly appreciated.
I have a set of entities defined as interfaces. Each has a concrete implementation and a wrapper implementation. For example:
-- Foo.java
public interface Foo {
public String getName();
}
-- FooImpl.java
public class FooImpl implements Foo {
private String name;
public String getName() {
return name;
}
}
-- AbstractWrapper.java
public class AbstractWrapper {
protected String decorate(String text) {
return "** " + text + " **";
}
-- FooWrapper.java
public class FooWrapper extends AbstractWrapper implements Foo {
private Foo wrappedFoo;
public FooWrapper(Foo wrappedFoo) {
this.wrappedFoo = wrappedFoo;
}
public String getName() {
return decorate(wrappedFoo.getName());
}
}
Now, the part that's complicating the situation is that I'm trying to make a List that automatically wraps the appropriate type with its appropriate wrapper before adding it. Something like:
-- WrappingList.java
/** T is the base entity interface type. */
public class WrappingList<T> implements List<T> {
private List<T> wrappedList;
public WrappingList(List<T> wrappedList) {
this.wrappedList = wrappedList;
}
....
public boolean add(T item) {
return wrappedList.add(wrapItem(item));
}
protected T wrapItem(T item) {
T wrappedItem = .....;
return T;
}
}
Is there anything I can do to make a somewhat clean factory method here? Or am I already out of luck at this point because of type erasure?
I think you should add a WrapperFactory to your WrappingList, and provide the actual factory implementation in the constructor. Something like:
-- WrapperFactory.java
public class WrapperFactory<T> {
public T wrap(T item);
}
-- WrappingList.java
/**
* A self-wrapping list.
*
* @param T the stored, wrapped item type
*/
public class WrappingList<T> implements List<T> {
private List<T> wrappedList;
private WrapperFactory<T> wrapper;
public WrappingList(List<T> wrappedList, WrapperFactory<T> wrapper) {
this.wrappedList = wrappedList;
this.wrapper = wrapper;
}
....
public boolean add(T item) {
return wrappedList.add(wrapper.wrap(item));
}
}
The basic problem is that erasure won't let you know anything about T in the actual code, so unless you pass something to your list that knows about the specific T you are using (e.g. how to wrap it) you won't be able to do anything. And it's not designing you in a corner ;) it's simply a common limitation of Java generics.