Alexey Andronov
Alexey Andronov

Reputation: 602

How to compare two classes

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):

Error

'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

Answers (2)

user719662
user719662

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

Holger
Holger

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

Related Questions