dimanche 11 juin 2017

How can I cast a Class

I have a class that looks like the following:

class FooClassAnalyser<T extends Foo> extends ClassAnalyser<T>

(where ClassAnalyser is an abstract base class for a number of concrete implementations; and FooClassAnalyser is a concrete implementation that's specialised for the case in which T extends Foo). It has a constructor that looks like this:

FooClassAnalyser(Class<T> classToAnalyse)

In another class, I have a static factory method for ClassAnalysers that calls an appropriate constructor depending on the type of classToAnalyse:

static <U> ClassAnalyser<U> constructClassAnalyser(Class<U> classToAnalyse)

The functionality I want is to check to see if U instanceof Foo, then construct a FooClassAnalyser and return it if it is.

However, I can't find a way to fit this within Java's type system. Type erasure means that we can't do anything clever with U directly. However, the fact that we pass classToAnalyse in as an argument makes it possible to test to see if U instanceof Foo via using reflection:

if (Foo.class.isAssignableFrom(classToAnalyse))

My problem is that unlike instanceof, this "instanceof via reflection" isn't visible to Java's type system. In particular, passing classToAnalyse directly as an argument to FooClassAnalyser's constructor fails with a type mismatch because Java doesn't know that classToAnalyse is actually a Class<U extends Foo>.

The best solution I've found so far is to use an unchecked cast to make classToAnalyse a Class<? extends Foo> (it is actually checked, but Java's unaware that it's checked). That at least makes it possible to pass it as an argument to new FooClassAnalyser, and get a FooClassAnalyser<?> object in return. The problem, however, is that this doesn't then convert back into a ClassAnalyser<U>, because Java doesn't recognise that casting classToAnalyse to have a different generic bound nonetheless doesn't change the fact that the Class object is still the same object (and thus is still a Class<U>); in other words, all Java can see is a FooClassAnalyser<?> that it doesn't recognise is also a FooClassAnalyser<U>, and thus converting back requires another unchecked cast. The result is code that compiles and runs, but with numerous warnings about type safety.

Most of the other things I've attempted have been syntax errors (e.g. a variable of type Class<U extends Foo> can't be declared directly; Java doesn't parse that properly). It should be noted that I don't actually have an object of type U at any point; I'm attempting to analyse the class itself, and thus only have Class<U> objects to work with.

Is it possible to write code like this in a type-safe way?





Aucun commentaire:

Enregistrer un commentaire