I have the following method:
public Comparator<T> getComparator() throws ReflectiveOperationException {
String className = "some.ClassName";
Class<Comparator<T>> c = Class.forName(className); // (1)
return (Comparator<T>) c. newInstance();
}
In the line (1) I get this error:
Type mismatch: cannot convert from Class <capture#1-of ?> to
Class<Comparator<T>>
What's wrong in this code and how should I make an instance of Comparator<T>?
The best you can get so far is
public <T> Comparator<T> getComparator() throws ReflectiveOperationException {
Class<? extends Comparator> implementation
= Class.forName("some.ClassName").asSubclass(Comparator.class);
#SuppressWarnings("unchecked")
final Comparator<T> c = implementation.newInstance();
return c;
}
Note that there is still an unchecked operation which is unavoidable. The runtime type token Comparator.class produces a Class<Comparator> rather than Class<Comparator<T>> which reflects the type erasure and implies that you can use it via asSubclass to ensure that a Class indeed implements Comparator, but you can’t ensure that it implements Comparator<T> regarding any <T>. (Note that this method doesn’t even know what T is). Therefore there is still an unchecked conversion of Comparator to Comparator<T>.
Simply move the cast:
Class<Comparator<T>> c = (Class<Comparator<T>>) Class.forName(className); // (1)
return c.newInstance();
Try this solution
#SuppressWarnings("unchecked")
public Comparator<T> getComparator() throws Exception {
String className = "some.ClassName";
Class<?> c = Class.forName(className); // (1)
return (Comparator<T>) c. newInstance();
}
Related
Given a generic class Foo<T>, I would like to create a static method valueOf(String s). The String s should hold the name of a class (e.g. "java.lang.Integer"). A call to valueOf("java.lang.Integer") should then return a new object Foo<Integer>.
In my class (see code below), I want to store the Class<T>, which is passed as parameter to the constructor Foo(Class<T> clazz). But in the valueOf-method, I am not sure what parameter to pass to the constructor Foo(Class<T> clazz) of the object I like to return.
Since I am relatively new to java reflections, I am unsure how I can implement this logic to my generic class. I have learned that the Class.forName(String) method returns an object Class<?>, but I don't know how to find out the type that the wildcard ? stands for (in case that's possible using reflections), or am I wrong going into this direction?
public class Foo<T> {
private Class<T> clazz;
public Foo(Class<T> clazz) {
this.clazz = clazz;
}
public static <E> Foo<E> valueOf(String s) throws ClassNotFoundException {
Class<?> clazz = Class.forName(s);
// here i am stuck. I would like to return new Foo<E>,
// but what is E, E's class and therefore the parameter for the constructor?
}
}
You can't do this with generics. Generics are just casts the compiler puts in for you, statically. So, if you couldn't do this with casts you write in the code (that is, you can't have a different cast for different strings), you can't do it with generics either.
You need some sort of key instead of a String which conveys the type T/E. You can pass in the Class<E> directly:
public static <E> Foo<E> valueOf(Class<E> clazz) throws ClassNotFoundException {
// ...
}
Not sure how to put it in words, so I'll just start with the code I currently have:
// "root type" for all resources
// fixed
public class ResourceClassA
{ }
// "base type" for all resources for a specific domain
// fixed
public class ResourceClassB extends ResourceClassA
{ }
// specific resource type
// can be derived from further but don't think that matters here
// not fixed but heavily constrained in other ways
public class ResourceClassC extends ResourceClassB
{ }
// only needed for a negative example below, irrelevant otherwise
public class ResourceClassD extends ResourceClassB
{ }
// fixed
public class Remote
{
public <T extends ResourceClassA> Set<T> read(Class<T> baseType, Class<? extends T> subType);
}
// semi-fixed: read() signature can be modified
public interface AbstractRemoteAccess<T extends ResourceClassA>
{
Set<T> read(Class<? extends T> clazz);
}
// semi-fixed: read() signature can be modified
public class SpecificRemoteAccess<T extends ResourceClassA> implements AbstractRemoteAccess<T>
{
private Class<T> _baseType;
private Remote _remote;
public Set<T> read(Class<? extends T> clazz)
{
return _remote.read(_baseType, clazz);
}
}
// not fixed
public class ConsumerClass
{
public void doSomething(AbstractRemoteAccess<ResourceClassB> remoteAccess)
{
Set<ResourceClassB> rawObjects = remoteAccess.read(ResourceClassC.class);
Set<ResourceClassC> castedObjects = rawObjects.stream()
.map(c -> (ResourceClassC) c)
.collect(Collectors.toSet());
}
}
All classes marked with fixed cannot be changed, they are provided as is - vice versa for not fixed. Class SpecificRemoteAccess is the one I'm looking to change: I would like the read() method to
not return its result as Set but as a Set<> of generic type matching clazz
so that the caller does not have to cast the method's result, see ConsumerClass.doSomething()
and all of this without loosing type-safety
The easiest way I saw was to do
Set<V> read(Class<V extends T> clazz)
but that produces this error:
Incorrect number of arguments for type Class<T>; it cannot be parameterized with arguments <V, T>
which, if I'm interpreting it correctly, means the compiler is treating V & T as separate type arguments for Class which doesn't match its definition.
Next I tried adding a second generic type V and using it as generic type for the return type of read(). I started with
<V extends T> Set<V> read(Class<? extends T> clazz)
which doesn't constrain V to clazz at all, meaning both of these will be accepted by the compiler
Set<ResourceClassC> correct = remoteAccess.read(ResourceClassC.class);
Set<ResourceClassD> incorrect = remoteAccess.read(ResourceClassC.class);
The type declaration for incorrect is semantically wrong but syntactically fine. So I tried to constrain V based on clazz but the only solution I could think of is
<V extends T> Set<V> read(Class<V extends T> clazz, Class<V> classV)
which does, somewhat, fix the problem from above:
// compiles
Set<ResourceClassC> correct = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
// error: Type mismatch: cannot convert from Set<ResourceClassC> to Set<ResourceClassD>
Set<ResourceClassD> incorrect = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
but not only does it make the read() call cumbersome (users will be wondering why they have to pass the same info twice) but also error prone:
// compiles
Set<ResourceClassC> correct = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
// type error
Set<ResourceClassD> incorrect = remoteAccess.read(ResourceClassC.class,
ResourceClassC.class);
// compiles but will cause run-time cast failures
Set<ResourceClassD> incorrect2 = remoteAccess.readAndCast2(ResourceClassC.class,
ResourceClassD.class);
Given consumer-side developers are faced with hundreds of resource classes like ResourceClassC, making read() error prone simply is no option.
Would appreciate if someone could point out my mistake.
I "simply" want to write a static generic method that takes the generic Collection<E> of any type E as its input, and outputs a result of the appropriate type Vector<E>. Since the type E is always known at compile-time, this should not be a problem - but it is... Thus, a call should later look like:
Collection<String> coll = ...
Vector<String> vec = Convert.toVector(coll); // either this or...
Vector<String> vec = Convert<String>.toVector(coll);
Here is what I tried - all without success:
import java.util.Collection;
import java.util.Vector;
public class Convert<E> {
// 1st try, same type E as class => Error: Cannot make a static reference to the non-static type E
public static Vector<E> toVector1(Collection<E> coll) {
return new Vector<E>();
}
// 2nd try, a new type X. => Error: X cannot be resolved to a type
public static Vector<X> toVector2(Collection<X> coll) {
return new Vector<X>();
}
// 3rd try, using wildcard. => Error: Cannot instantiate the type Vector<?>
public static Vector<?> toVector3(Collection<?> coll) {
return new Vector<?>();
}
// 4th try, using bounded wildcard. => Error: Cannot make a static reference to the non-static type E
public static Vector<? extends E> toVector4(Collection<? extends E> coll) {
return new Vector<E>();
}
}
Is this not possible at all? And if not, is there a good reason why not? Or am I just doing it wrong? Probably there is some solution using Lambda expressions?
You should give your static method its own generic type parameter :
public static <T> Vector<T> toVector1(Collection<T> coll) {
return new Vector<T>();
}
You were missing the generic type parameter declaration (<T>) before the return type of the method.
From the JDK documentation: "For static generic methods, the type parameter section must appear before the method's return type.". So it should look like
public static <E> Vector<E> toVector1(Collection<E> coll) {
return new Vector<E>();
}
// 1st try, same type E as class => Error: Cannot make a static reference to the non-static type E
public static Vector<E> toVector1(Collection<E> coll) {
return new Vector<E>();
}
This is because you've already defined a type-parameter, called E, on instance context and the compiler doesn't allow you to use it on static context.
// 2nd try, a new type X. => Error: X cannot be resolved to a type
public static Vector<X> toVector2(Collection<X> coll) {
return new Vector<X>();
}
Here, even though you don't use the instance type-parameter E, but another one, called X, the former is not correctly defined. When introducing method-scoped type-parameters, you have to do:
public static <X> Vector<X> toVector2(Collection<X> coll) {
return new Vector<X>();
}
// 3rd try, using wildcard. => Error: Cannot instantiate the type Vector<?>
public static Vector<?> toVector3(Collection<?> coll) {
return new Vector<?>();
}
The error is simply because the wildcard <?> can be only used in return-types and on initialization, but not on instantiation (like you've done).
// 4th try, using bounded wildcard. => Error: Cannot make a static reference to the non-static type E
public static Vector<? extends E> toVector4(Collection<? extends E> coll) {
return new Vector<E>();
}
The reason is the same as 1st try. You can fix this by having:
public static <X> Vector<? extends X> toVector4(Collection<? extends X> coll) {
return new Vector<X>();
}
However, note that when you use this method, you won't be able to add anything but null to the resulting list.
This program does not compile:
public class xx {
static class Class1<C> {
void method1(C p) {
}
}
static class Class2<T> extends Class1<Class<? extends T>> {
T object;
void method2() {
this.method1(this.object.getClass());
}
}
}
The error is:
xx.java:10: method1(java.lang.Class<? extends T>) in xx.Class1<java.lang.Class<? extends T>>
cannot be applied to (java.lang.Class<capture#215 of ? extends java.lang.Object>)
this.method1(this.object.getClass());
Why does this happen? Why does the compiler seemingly believe that object.getClass() returns Class<? extends Object> instead of Class<? extends T> ?
There is no upper bound set on T in your code, so ? extends T is really tantamount to ? extends Object. Just yesterday I played with a similar example and hit this barrier. I had
static <T> T newInstance(T o) throws Exception {
final Class<? extends T> c = o.getClass();
return c.newInstance();
}
and it complained with the same error. Consider this: the return type of Object.getClass() is Class<?> and the compiler will want to capture the ? into a concrete type. But instead, we would like not to capture the ?, but to "capture the upper bound" T -- and there is no such thing in Java's generics.
Object.getClass() is defined to return a Class<? extends |T|>, where T is the statically known type of the receiver (the object getClass() is called on). Take special note of the vertical bars, the erasure operator. The erasure of a type variable is the erasure of its leftmost bound. In your case that's the implicit bound Object. So you get back a Class<? extends Object>, not a Class<? extends T>.
Why is that?
Imagine T = List<Integer>, you could suddenly do the following without unchecked warning:
List<String> myStrings = new ArrayList<>();
List<Integer> myInts = new ArrayList<>();
List<Integer> myIntyStrings = myInts.getClass().cast(myStrings);
myIntyStrings.add(-1);
String myString = myStrings.get(0); // BANG!
But thankfully we do get a warning.. ;)
According to the documentation on getClass(), the returned object has type Class< ? extends |X| >, where |X| is the erasure of the type of the instance on which the method is called.
Therefore calling getClass() on an object of type T returns Class< ? extends Object >. We have no bound information about T in this API.
Usually APIs which use reflection on generic classes require that the client pass an additional argument of type Class< T > to the constructor or generic method in question.
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;