Reputation: 602
I'm trying to create a factory, here's the code:
public class MyFactory {
public static <T> MyBase create(Class<T> _c) {
if (_c.getClass().equals(Derived1.class)) // (1)
return createNewDerived1();
return null;
}
}
// caller
MyFactory.create(Derived1.class);
It compiles with warning at line (1)
:
'equals()' between objects of inconvertible types 'Class<Derived1>'
and 'Class<capture of ? extends Class>' Reports calls to .equals()
where the target and argument are of incompatible types. While such a
call might theoretically be useful, most likely it represents a bug.
And in runtime my if
statement fails for some reason.
How can I get the intended behaviour?
Upvotes: 2
Views: 3296
Reputation:
public class MyFactory {
public static <T> MyBase create(Class<T> _c) {
return ( _c == Derived1.class ) ? createNewDerived1() : null;
}
}
// caller
MyFactory.create(Derived1.class);
See Compare class objects for further info.
FIY, "real" class object references have Class<MyClass>
type signatures (before erasure), while the Class
returned by .class
class is, AFAIR, just rawtype Class
on some environments and Class<capture of ?>
-ish on others.
Upvotes: 4
Reputation: 298539
The problem of your code
public static <T> MyBase create(Class<T> _c) {
if (_c.getClass().equals(Derived1.class)) // (1)
return createNewDerived1();
return null;
}
is that you want to compare the class represented by _c
with the literal Derived1.class
, but you have an unnecessary getClass()
invocation on the Class
object. Invoking getClass
on a Class
object will return the object representing the class java.lang.Class
, which you surely don’t want to compare with Derived1
, so the warning is a good thing.
The solution is to remove the getClass()
invocation. If you replace equals
with an identity comparison, ==
, like in this answer, it doesn’t hurt. Classes are always represented by a single canonical Class
instance, so the semantic of equals
and ==
is the same for Class
instances.
But note that your generic signature is questionable.
If the provided Class
object is intended to specify the type of the created instance, but the return type is always MyBase
, you surely want to restrict the parameter to be assignable to MyBase
, e.g.
public static <T extends MyBase> MyBase create(Class<T> _c) {
if(c.equals(Derived1.class))
return createNewDerived1();
return null;
}
but you don’t need a type parameter here; changing the signature to
public static MyBase create(Class<? extends MyBase> _c)
bears the same semantic.
Or you decide to specify that the returned type is guaranteed to be of the type specified by the parameter (if that’s the intention). Then, you should change the method to
public static <T extends MyBase> T create(Class<T> _c) {
return c==Derived1.class? _c.cast(createNewDerived1()): null;
}
or
public static <T extends MyBase> T create(Class<T> _c) {
return c==Derived1.class? _c.cast(createNewDerived1()): _c.newInstance();
}
etc.
which you can use like
Derived1 obj = MyFactory.create(Derived1.class);
Upvotes: 2