I am very new to Java generics and Class<> clazz type, I have an interface
public interface MyInterface {
void doSomething(String arg1);
}
which is implemented by multiple classes
class MyInterfaceImplFirst implements MyInterface {
private arg;
public MyInterfaceImplFirst(String arg) {
this.arg = arg;
}
@Override
void doSomething(String arg1) {
//...
}
}
class MyInterfaceImplSecond implements MyInterface {
private arg;
public MyInterfaceImplSecond(String arg) {
this.arg = arg;
}
@Override
void doSomething(String arg1) {
//...
}
}
I have a class that accepts Class<MyInterface> type
public class Dummy {
public void load(Class<MyInterface> clazz) {
//...
}
public static void main(String[] args) {
Dummy dummy = new Dummy();
//The below does not compile
dummy.load(MyInterfaceImplFirst.class);
}
}
If I change the load method to public void load(Class clazz) it works fine, can someone tell me how to restrict arguments to load method to be only of subtypes(impl) of MyInterface ?
CodePudding user response:
PECS (Producer-Extends, Consumer-Super) is the problem. Given a List<Number>
, you can, of course, call .add(someDouble)
. Therefore, a List<Integer>
cannot be passed when a List<Number>
is required. This 'write' concept just doesn't apply to Class<X>
, but java doesn't 'know' that and therefore won't let you pass a Class<SomeImpl>
when a Class<TheInterface>
is required.
PECS is kind of a misnomer; it's written from the point of view of the genericsed type (so, List, Class, etc). If the class only produces stuff and will not be consuming anything of the generic type, which is the case here, PECS states you should be using extends
.
Hence, public void load<Class<? extends MyInterface> clazz)
is what you want.
A few notes:
- Generics serves to link things. If you declare a generic variable and you use it in 0 or 1 places, as the misleading comment from @RobSpoor erroneously suggests, then you're messing up or hacking java's type system (there are legitimate cases where that's a good idea, but quite rare). Given that you don't need that T anywhere, you'd be messing up. Just use
?
instead. - Mixing generics and
Class
is usually bad. There are thingsClass
can represent, such asint
(Class<?> c = int.class;
is valid java), which generics can't (List<int>
is not valid java), and vice versa things generics can represent that class cant. You cannot have an instance ofjava.lang.Class
that representsList<String>
(only List - the raw type).Class<List<String>>
isn't a thing;List<String>.class
does not work.List<List<String>>
does work. - Whatever made you think: I know! I'll make an interface, use generics, and make a
Class<? extends MyInterface>
- that was likely a mistake. You didn't explain what made you think you needed this. Most likely, the right answer is a factory. A factory is, essentially, an abstraction for constructors.