matafuego
matafuego

Reputation: 83

In a Transformer, how to specify that the types fo the classes being transformed are of the same type?

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

Answers (4)

Paul Bellora
Paul Bellora

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

irreputable
irreputable

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

Eyal Schneider
Eyal Schneider

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

zagyi
zagyi

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

Related Questions