Reputation: 83
I'm having a very simple yet confuse question at the same time.
In a Transformer, is there a way to specify that the types of the classes being transformed are the same type?
To make it clear, I'll share the code:
Transformer<Set<?>, List<?>> transformer = new SetToListTransformer();
Is there a way for me to specify that the Set and the List are of the same type?
Also when writing the transformer I did this, and I assume it serves no purpose:
private static class SetToListTransformer implements Transformer<Set<?>, List<?>> {
@Override
public List<?> transform(final Set<?> input) {
return this.doTransform(input);
}
public <T> List<T> doTransform(final Set<T> input) {
...
}
}
The thing is, I cannot type the SetToListTransformer since I do not really care about what types are inside, I just care that they are the same type.
Any help would be appreciated!
PS: I'm not really transforming a Set into a List, I'm using other types, I just used them to clarify the code :)
Upvotes: 1
Views: 838
Reputation: 55213
There's no way to enforce the desired constraint on your transform
implementation, since there is no way to impose any relationship between generic wildcards. Each of the two ?
in your SetToListTransformer
declaration are doomed to each mean some unknown type with no way to bound them to each other.
As others pointed out, the easiest solution is to make SetToListTransformer
generic. For example:
class SetToListTransformer<T> implements Transformer<Set<? extends T>, List<T>> {
@Override
public List<T> transform(final Set<? extends T> input) {
final List<T> output = new ArrayList<T>(input.size());
output.addAll(input);
return output;
}
}
Of course this requires you to instantiate transformers with specific type arguments. This should be fine as long as SetToListTransformer
is cheap. But, you indicated you just want to use one instance. Here's how to do that:
class MyTransformers {
// There is no reason to expose SetToListTransformer now.
// Keep it here as an anonymous class.
private static final Transformer<Set<?>, List<?>> FROM_SET_TO_LIST =
new Transformer<Set<?>, List<?>>() {
@Override
public List<?> transform(final Set<?> input) {
return doTransform(input);
}
private <T> List<T> doTransform(final Set<T> input) {
final List<T> output = new ArrayList<T>(input.size());
output.addAll(input);
return output;
}
};
private MyTransformers() { }
public static <T> Transformer<Set<? extends T>, List<T>> fromSetToList() {
@SuppressWarnings("unchecked")//this is okay for any T because the impl is stateless
final Transformer<Set<? extends T>, List<T>> withNarrowedTypes =
(Transformer<Set<? extends T>, List<T>>)(Transformer<?, ?>)FROM_SET_TO_LIST;
return withNarrowedTypes;
}
}
Here's a usage example:
Set<Integer> intSet = new HashSet<Integer>();
intSet.add(42);
intSet.add(1337);
List<Number> numList = MyTransformers.<Number>fromSetToList().transform(intSet);
Upvotes: 0
Reputation: 45433
You can't express that constraint with the Transformer
interface. You may create a subtype that imposes additional constraints, and use the subtype where Transformer
was used.
interface StricterTransformer extends Transformer<Set<?>, List<?>>
public <T> List<T> transform2(Set<T> input) ;
/** javadoc additional contract: must behave as transform2(input) */
public List<?> transform(Set<?> input);
// in java8 we can give a "default" impl for this interface method
// that simply calls transform2(), so subclasses don't have to impl
// the transform() method, which is just boiler plate code.
Upvotes: 0
Reputation: 22446
Make your class generic:
private static class SetToListTransformer <T> implements Transformer<Set<T>, List<T>> {
@Override
public List<T> transform(Set<T> input) {
return this.doTransform(input);
}
public List<T> doTransform(Set<T> input) {
...
}
}
Note, however, that this implementation is quite strict with types. You wont be able to use a SetToListTransformer<Number> to convert from Set<Integer> to List<Number>, although Integer IS a Number.
Upvotes: 1
Reputation: 17518
Try to bind both wildcards to the same type paramter, like this:
class SetToListTransformer<E> implements Transformer<Set<E>, List<E>> {
@Override
public List<E> transform(Set<E> from) {
...
}
}
Upvotes: 1