Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Return class literal as intersection type

I have couple of enums implementing some common interface and I would like to return class literal from the method. However I am unable to specify the intersection type correctly. See below the code sample illustrating the problem.

public class GenericsTest {    
  interface Iface {   
  }

  enum E1 implements Iface {    
  }

  enum E2 implements Iface {    
  }

  <E extends Enum<E> & Iface> Class<E> getEnum1() {
    return E1.class;  //ERROR incompatible types: java.lang.Class<GenericsTest.E1> cannot be converted to java.lang.Class<E>
  }

  Class<? extends Enum<?>> getEnum3() {
    return E1.class;  //OK
  }

  Class<? extends Iface> getEnum4() {
    return E1.class;  //OK
  }

  <E extends Enum<E> & Iface> void enumParam(Class<E> p) {
    enumParam(E1.class);  //OK
  }

}

#getEnum1 method doesn't compile. Interestingly it works as parameter value in #enumParam. How can I specify the intersection type to be able to return class literal from the method?

like image 974
Tibor Blenessy Avatar asked Aug 26 '16 12:08

Tibor Blenessy


1 Answers

The method getEnum1 does not compile for literally the same reason as this one:

public <E> E foo() {
    return "bar"; // Compile time error: Incompatible types E and String
}

See? Just because the type parameter E could be eventually a string, it does not mean it is compatible with String. One could call this function as Integer i = x.<Integer>foo();, so returning a string does not make sense here.

getEnum1 is quite the same. Type parameter E could be choosen to be the type E1 (very bad naming here), but it does not mean E1 is always compatible with E. Consider E2 e2 = x.<E2>getEnum1();. You may wonder why the compiler doesn't just alert an error when one tries to call it with the wrong type parameter. The thing is, E2 satisfies the type constraints, so there is just no reason to show an error there.

So how can you make your code working?

Easiest (and possibly the only) way is to just get rid of generics there:

Class<E1> getEnum1() {
  return E1.class;
}

If you don't want to expose the concrete type E1 here, you are out of luck. All you can do is to extract the common functionalities to an interface, and return that type.

like image 102
Tamas Hegedus Avatar answered Oct 27 '22 06:10

Tamas Hegedus