9

I know that I can't do this:

public abstract class DTODomainTransformer<T, S> {

    public abstract S transform(T);

    public abstract T transform(S);

} 

Because I get the compiler complaint:

Method transform(T) has the same erasure transform(Object) as another method in type Transformer<T,S>

I understand that is because both T and S could be extending same class. So doing this way i can tell him "No, they are not the same, so take it easy"

public interface Transformer<T extends AbstractDTO , S extends AbstractDomain> {

    public abstract S transform(T object);

    public abstract T transform(S object);

}

Then, my question is, is there any way to tell the compiler that T and S extend from different classes without telling which ones in concrete? I mean, in this last case, I've specified which classes had to be T and S (extending respectively). But what if I want it more generic and not specify them? I'd like to tell the compiler, "Hey, compiler, T and S are not the same! They are different classes. I don't know exactly which classes they are, but I'm sure that they are different".

8
  • 8
    No, there is no way to do that without giving explicit bounds. Commented Mar 19, 2015 at 20:08
  • How can you - programmer - assure the user of your class won't violate that rule, in case he/she uses raw type? Commented Mar 19, 2015 at 20:09
  • If it helps, you only have bind one of them. <T extends Foo, S> Commented Mar 19, 2015 at 20:15
  • 3
    You really ought to be using a different method name for each direction. Commented Mar 19, 2015 at 21:56
  • 1
    You could use Guavas Converter class as your parent class. It already contains what you need and you don't have to care about clashing methods, due to the different method names. Commented Mar 19, 2015 at 23:12

1 Answer 1

3

There's not an obvious way. (Although you can build one, as I show below.)

This overload rule is due to a limitation of how the supertype (in this case, interface) that declares the overloads gets translated (by erasure) to bytecode.

If there's a generic parameter declared T, a method that uses T in its signature will have bytecode generated as the upper bound of T, for example

class Generic<T> {
    void work(T t) {}
}

will get erased to

class Generic {
    void work(Object t) {}
}

and

class Generic<T extends Number> {
    void work(T t) {}
}

will get erased to

class Generic {
    void work(Number t) {}
}

This is how the bounded example works, because the overloads erase differently.

public interface Transformer {
    public abstract AbstractDomain transform(AbstractDTO object);
    public abstract AbstractDTO transform(AbstractDomain object);
}

But without specific bounds, what erased bytecode should be generated for the overloaded methods?

So your T and S being different on the subtype is not what's important. What is important is the known declared bounds which get translated to erased bytecode for the supertype class.


A possible solution could use marker interfaces.

interface TransformT {}
interface TransformS {}
interface Transformable extends TransformT, TransformS {}

interface Transformer<T extends TransformT, S extends TransformS>
    T transform(S s);
    S transform(T t);
}

abstract class AbstractDTO implements Transformable {}
abstract class AbstractDomain implements Transformable {}

new SomeTransformerImpl<AbstractDTO, AbstractDomain>()

But I don't necessarily recommend doing this. It seems elaborate to me, although interesting. It depends on how complicated the actual class hierarchy is.

What Louis suggested in the comments is much simpler: give the methods different names.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.