1

How would I express the following type relationship in Java generics:

class MyClass<T> { }

interface MyInterface<T extends MyClass> { 
  void m1(T<Integer> argument);
  void m2(T<String> argument);
  void m3(T<?> argument);
}

without a compiler complaint. Is that even possible? I want to make sure that all methods receive the same (sub)class of MyClass while not breaking type-safety. In the domain, different subclasses og MyClass would not make sence. Do I really have to list three generic type variables like this:

class MyClass<T> { }

interface MyInterface<T1 extends MyClass<Integer>, 
   T2 extends MyClass<String>, T3 extends MyClass<?>> { 
  void m1(T1 argument);
  void m2(T2 argument);
  void m3(T3 argument);
}

I feel like this is terrible to read and does neither express my intention as nicely as I wished. This question is related to my other question on generics Raw types inside of generic definition which still confuses me. Maybe someone can help! Thank you.

Community
  • 1
  • 1
Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192
  • 2
    You definitely can't do `T<>` with Java generics. Your solution is probably the best available workaround. – Louis Wasserman Jun 14 '13 at 17:28
  • Well, the problem is that I have an interface with arround 20 different generic types... I hate using this interface and yet I need it. Either users dont get type safety or they type forever, both solutions are not quite it. – Rafael Winterhalter Jun 14 '13 at 17:32

2 Answers2

0

Not sure if its a good way. I feel using below method you cannot "bind" the parameters m1, m2, m3 method, instead while calling you can have privilege of using any kind of parameters be it MyClass<Integer> or MyClass<String>

public class MyClass<T> { }

interface MyOtherClass<T extends MyClass<?>> { 
  void m1(T argument);
  void m2(T argument);
  void m3(T argument);
}

And the implementer

class MyOtherClassImpl<T extends MyClass<?>> implements MyOtherClass<T> {

    @Override
    public void m1(T argument) {

    }

    @Override
    public void m2(T argument) {

    }

    @Override
    public void m3(T argument) {

    }

}

and this is how you can call

  MyOtherClass<MyClass<?>> otc = new MyOtherClassImpl<MyClass<?>>();
  MyClass<Integer> m1 = new MyClass<Integer>();
  MyClass<String> m2 = new MyClass<String>();
  MyClass<Object> m3 = new MyClass<Object>();
  otc.m1(m1);
  otc.m2(m2);
  otc.m3(m3);
sanbhat
  • 17,522
  • 6
  • 48
  • 64
  • The problem with this solution is that I lose all type safety within `MyInterface`. I just have to assume that the constraints are set correctly and will otherwise get nasty `ClasscastException`s at runtime if not applied correctly. Also, this won't work when using the generic parameter as a return type when type-safety is of matter for the end user. – Rafael Winterhalter Jun 15 '13 at 08:30
0

Well, I realized that I get because I cannot express this in Java:

class MyClass<T> { }

interface MyInterface<T extends MyClass> { 
  T<S> m1(S argument);
}

However, I found a rather fancy solution to this. Instead of manually upgrading my return type in every subinterface, I use Spoon to read and write the corresponding source code before compilation. The compiler sees my code as if I overrode the return types in every interface manually. This keeps my code in sync with the super interface on every Maven build and I do not have to worry about it any more.

This solution might not be for everybody, but it is the cleanest solution I could think of in the spirit of DNRY.

Community
  • 1
  • 1
Rafael Winterhalter
  • 42,759
  • 13
  • 108
  • 192