Java: How to create a specific class instance? - java

I have a method with signature
<T extends AbstractClass> T method(Class<T> abstractClass)
and I cannot modify the signature.
Also I have a string with class name com.test.MyClass. Is it possible to create a class by class name to pass to my method?
Something like that
Class<? extends AbstractClass> clz = (Class<? extends AbstractClass>) Class.forName(classNameStr);

If you want a type safe dynamic class loading, the correct way is:
Class<? extends AbstractClass> clz =
Class.forName(classNameStr).asSubclass(AbstractClass.class);
This does not generate “unchecked” warnings as it is checked at runtime in the asSubclass method and afterwards, e.g. calling newInstance an that Class is guaranteed to return an instance of AbstractClass.
So afterwards you can do
AbstractClass obj = method(clz);
Of course, you can inline the construct as
AbstractClass obj=method(Class.forName(classNameStr).asSubclass(AbstractClass.class));
but it should be obvious why I wouldn’t recommend it.

Related

Unchecked typecasting of generic class?

I am trying to write a method where I can convert from a string to an enum object at runtime, for a generic enum. I have a method signature:
public static <T extends Enum<T>> Enum<T> foo(String string, Class<T> clazz)
However, I am calling it from a class whose generic type parameter does not explicitly extend Enum. i.e.
class bar<X> {
private Class<X> clazz;
if (XIsAnEnum()) {
foo(string, clazz)
}
}
This does not compile because even though I know, from the logic of XIsAnEnum, that X extends Enum<X>, I don't explicitly state this in the generic type parameter definition, so it is not a valid argument.
Is there a way to do an unchecked cast from Class<X> to Class<X extends Enum<X>>, or will I have to make a new class bar2<X extends Enum<X>> specifically for when I want to use enums?
You can use Class#asSubclass(Class) to do the cast for you, like
foo("value", clazz.asSubclass(Enum.class));
This involves an actual verification that clazz is referring to a Class that is a subclass of Enum.
You're throwing out all the generic verification here though.

Java: Inheriting class can not be converted to super class in method parameter

I have this method with a generic class as parameter:
myMethod(Class myclass){
Superclass superclass = myclass;
}
then I use the method by passing a child class of the Superclass
myMethod(Mychildclass.class)
Netbeans is giving me a warning that I am using generic "Class". However this works fine.
If I change the method's parameter to this more specific
myMethod(Class<Superclass> myclass){
this.superclass = myclass;
}
then I am getting an error when trying to use my method:
incompatibles types: Class<Mychildclass> cannot be converted to Class<Superclass>
So my question: Why is this not working? How can I make Netbeans happy giving me no warning and no error messages?
Try this instead:
private Class<? extends Superclass> superclass;
void myMethod(Class<? extends Superclass> myclass){
this.superclass = myclass;
}
If you use Class<Superclass>,
only Superclass.class is expected.
If you use Class<? extends Superclass>,
Superclass.class or any of its child classes are expected.

What does Class<? extends ParentClass> mean?

I found a method like the one below.
public void simpleTest(Class <? extends ParentClass> myClass){
}
I didn't understand the expression : Class <? extends ParentClass> myClass here.
Can anyone explain it?
Class <? extends ParentClass> myClass is a method argument whose type is a Class that is parameterized to ensure that what's passed is a Class that represents some type that is a subtype of ParentClass.
i.e. given:
class ParentParentClass {}
class ParentClass extends ParentParentClass {}
class ChildClass extends ParentClass {}
class ChildChildClass extends ChildClass {}
public void simpleTest(Class <? extends ParentClass> myClass) {}
These are valid:
simpleTest(ParentClass.class);
simpleTest(ChildClass.class);
simpleTest(ChildChildClass.class);
These aren't valid because the argument doesn't "fit" inside the required type:
simpleTest(ParentParentClass.class);
simpleTest(String.class);
simpleTest(Date.class);
simpleTest(Object.class);
Class myClass - means that myClass should be a a sub type of ParentClass.
class MyClass extends ParentClass {
}
simpleTest(ParentClass.class); // this is ok
simpleTest(MyClass.class); // this is ok
class SomeOtherClass {
}
simpleTest(SomeOtherClass.class); // will result in compiler error
From the javadoc of java.lang.Class<T>:
T - the type of the class modeled by this Class object. For example, the type of String.class is Class<String>. Use Class<?> if the class being modeled is unknown.
Now replace String with your class: it's a Class of a type which is a ParentClass or a subclass of ParentClass.
It's a generic method taking a parametrized class.
It might be helpful to first consider a simpler generics example: List<? extends ParentClass> means it's a List that only takes objects that fulfill the is-a relationship with ParentClass. I.e. the parameter of the List implementation is-a ParentClass, but the type of the whole thing List<? extends ParentClass> is List.
In your example, just substitute "Class" instead of "List". myClass is a Class, i.e. you can call the method with something like "MyClassName.class". Further, the parameter of the Class of my Class is-a ParentClass. This basically means that you can only pass the Class of ParentClass or its subclasses to the method.
See also: http://docs.oracle.com/javase/7/docs/api/java/lang/Class.html

Generics and Class.asSubclass

I've always thought the following should work. I get an object which I know is a Class<X> where X extends some class Xyz. In order to make it type-safe I wanted to use Class.asSubclass like in the following method:
private Class<? extends Xyz> castToXyzClass(Object o) {
final Class<?> resultClass = (Class<?>) o;
final Class<? extends Xyz> result = Xyz.class.asSubclass(resultClass);
return result;
}
However, in Eclipse it doesn't work, the only solution I see is an unchecked cast. I'd bet the above code must work, I've used something like this already... no idea what's wrong here.
asSubclass() operates on the object it's called on, not on its parameter - not what one is used to, but it reads quite well. You just have to do this:
final Class<? extends Xyz> result = resultClass.asSubclass(Xyz.class);
The asSubclass is a bit of a confusing name because you're not obtaining a Class object representing the subclass, you're obtaining the same class object that is retyped to reflect that it is a subclass of some parent class.
In fact, this method is fairly single purpose (and I think you've found it)...it's to take a raw or wildcarded class and get a better type parameter with a runtime check. It's not needed when you don't have gaps in your type information:
class Super {}
class Sub extends Super {}
//...
Class<Sub> subClass = Sub.class;
//both work, but the latter introduces a redundant runtime check
Class<? extends Super> subOfSuper1 = subClass;
Class<? extends Super> subOfSuper2 = subClass.asSubclass(Super.class);

Class<T> and static method Class.forName() drive me crazy

this code doesn't compile. I'm wondering what I am doing wrong:
private static Importable getRightInstance(String s) throws Exception {
Class<Importable> c = Class.forName(s);
Importable i = c.newInstance();
return i;
}
where Importable is an interface and the string s is the name of an implementing class.
The compiler says:
./Importer.java:33: incompatible types
found : java.lang.Class<capture#964 of ?>
required: java.lang.Class<Importable>
Class<Importable> c = Class.forName(format(s));
thanks for any help!
All the solutions
Class<? extends Importable> c = Class.forName(s).asSubclass(Importable.class);
and
Class<? extends Importable> c = (Class<? extends Importable>) Class.forName(s);
and
Class<?> c = Class.forName(format(s));
Importable i = (Importable)c.newInstance();
give this error (that i don't understand):
Exception in thread "main" java.lang.IncompatibleClassChangeError: class C1
has interface Importable as super class
where C1 is actually implementing Importable (so it is theoretically castable to Importable).
Use a runtime conversion:
Class <? extends Importable> c
= Class.forName (s).asSubclass (Importable.class);
This will bark with an exception at runtime if s specifies a class that doesn't implement the interface.
Try:
Class<? extends Importable> klaz = Class.forName(s).asSubclass(Importable.class);
Here are some snippets to illustrate the problems:
Class<CharSequence> klazz = String.class; // doesn't compile!
// "Type mismatch: cannot convert from Class<String> to Class<CharSequence>"
However:
Class<? extends CharSequence> klazz = String.class; // compiles fine!
So for an interface, you definitely need the upper-bounded wildcard. The asSubclass is as suggested by doublep.
API links
<U> Class<? extends U> asSubclass(Class<U> clazz)
Casts this Class object to represent a subclass of the class represented by the specified class object. Checks that that the cast is valid, and throws a ClassCastException if it is not. If this method succeeds, it always returns a reference to this class object.
Related questions
What is the difference between <E extends Number> and <Number>?
See also
Java Tutorials/Generics/Subtyping
More fun with wildcards
Something like this might do the trick:
Class<?> c1 = Class.forName(s);
Class<? extends Importable> c = c1.asSubclass(Importable.class);
return c.newInstance();
Beware of a ClassCastException or NoClassDefFound if you pass in the wrong thing. As #polygenelubricants says, if you can figure out some way to avoid Class.forName then so much the better!
The issue is Class.forName is a static method with the following definition
public static Class forName(String className) throws ClassNotFoundException
Therefore it is not a bound parameter type at all and compiler would definitely throw the cast warning here as it has no way to guarantee the string name of the class would always give you the implementation of the interface.
If you know for sure that the string name passed into the method would be an implementation of the interface you can use SuppressWarnings annotation. But I dont think ther eis any other cleaner way to use forName with generics
where Importable is an interface and the string s is the name of an implementing class.
The compiler can't know that, hence the error.
Use a cast. It is easier to cast the constructed object (because that is a checked cast), than the class object itself.
Class<?> c = Class.forName(s);
Importable i = (Importable) c.newInstance();
return i;

Categories

Resources