Generics and forName()


T

Tony Morris

Jacob said:
If I have this class:

class A {
public A(String a);
}

Is it possible to make an instance through reflection without getting a
compiler warning (java 1.5)?

The following gives an unchecked warning in the first line due to the
forced cast. Using "?" just postpone the the problem:

Class<A> clazz = (Class<A>) Class.forName("A");
Constructor<A> ctor = clazz.getConstructor(String.class);
A a = ctor.newInstance("hello world");

The problem is that in my organization we treat warnings as errors and
the above simply isn't doable within such a regime.

What is the workaround?

(And why whould all the above be possible (by Class<?> and Class.newInstance()),
if just the constructor didn't take any arguments?)

Thanks!

Hello Jacob,
I have seen this question quite a number of times, which prompted me to add
it to my FAQ.
http://jqa.tmorris.net/GetQAndA.action?qids=80&showAnswers=true

I have also included a code sample below that demonstrates what you want to
achieve.
As a note, if you have a policy of not being permitted to have compile-time
warnings, it may be in your interest to learn about generics.

Also note that there are some valid use cases that force a compile-time
warning, due to the contrived nature of the Java generics implementation, so
you may seek to have this policy reviewed. These forced use cases are the
result of the introduction of failure to maintain a conversion between
arrays and parameterised types (without a warning) - as an example, take a
look at the java.util.ArrayList implementation, which is backed by an
array - you will note that there is a forced compile-time warning. You
discover these nitty details when you implement the spec. after a JSR
"expert" group has been onto the case :) I did make this issue known well
before the spec. was released, but the most plausible response I got was
"oops".

import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import static java.lang.Class.forName;

class X
{
X(final String s)
{

}
}

final class Main
{
private Main() throws UnsupportedOperationException
{
throw new UnsupportedOperationException();
}

public static void main(final String... args) throws
ClassNotFoundException,
NoSuchMethodException,
InstantiationException,
IllegalAccessException,
InvocationTargetException
{
final Class<?> c = forName("X");
final Class<? extends X> xc = c.asSubclass(X.class);
final Constructor<? extends X> ctor =
xc.getConstructor(String.class);
final X x = ctor.newInstance(args[0]);
}
}


--
Tony Morris
http://tmorris.net/

Java Questions and Answers
http://jqa.tmorris.net/
 
Ad

Advertisements

J

Jacob

If I have this class:

class A {
public A(String a);
}

Is it possible to make an instance through reflection without getting a
compiler warning (java 1.5)?

The following gives an unchecked warning in the first line due to the
forced cast. Using "?" just postpone the the problem:

Class<A> clazz = (Class<A>) Class.forName("A");
Constructor<A> ctor = clazz.getConstructor(String.class);
A a = ctor.newInstance("hello world");

The problem is that in my organization we treat warnings as errors and
the above simply isn't doable within such a regime.

What is the workaround?

(And why whould all the above be possible (by Class<?> and Class.newInstance()),
if just the constructor didn't take any arguments?)

Thanks!
 
C

Chris Uppal

Jacob said:
The problem is that in my organization we treat warnings as errors and
the above simply isn't doable within such a regime.

Ha ha! Then you loose!

I'm not sure about the particular example you mention -- I don't know a
(working) way to avoid the warning myself, but that doesn't mean there isn't
one[*].

But the real problem here is not javac, but the way your organisation works.
Find the person responsible for this absurd rule and fire him/her (and,
perhaps, also his/her manager). Or if the condition was imposed by a customer,
find the salesthing which accepted that clause in the contract and fire it.

-- chris

[*] Well, I suppose you could compile with -source 1.4
 
R

Roedy Green

The following gives an unchecked warning in the first line due to the
forced cast. Using "?" just postpone the the problem:

Class<A> clazz = (Class<A>) Class.forName("A");
Constructor<A> ctor = clazz.getConstructor(String.class);
A a = ctor.newInstance("hello world");

Unchecked means the compiler cannot guarantee safety at compile time.
The truth is it can't. It has absolutely no idea what A will be/do. If
you want the compiler to be able to check, it wants to see the code at
compile time.

Further this code makes no sense to me. If type A is known at compile
time, why are you dicking around with Class.forName. You only do that
when you DON'T know the class name, just an interface it implements..
 
I

Ian Pilcher

Jacob said:
If I have this class:

class A {
public A(String a);
}

Is it possible to make an instance through reflection without getting a
compiler warning (java 1.5)?

The following gives an unchecked warning in the first line due to the
forced cast. Using "?" just postpone the the problem:

Class<A> clazz = (Class<A>) Class.forName("A");
Constructor<A> ctor = clazz.getConstructor(String.class);
A a = ctor.newInstance("hello world");

The problem is that in my organization we treat warnings as errors and
the above simply isn't doable within such a regime.

In this case, you can use Class.asSubclass:

Class<A> clazz = Class.forName("A").asSubclass(A.class);

As Tony pointed out, there are perfectly valid (and sometimes
unavoidable) constructs which will produce unchecked cast warnings.
Read up on the @SuppressWarnings annotation, and note that Sun's
compiler didn't support it until 1.5.0_06.

I'm assuming that the code you posted is a contrived example. If not,
Roedy's comment is right on; there's no reason to use Class.forName for
a known type.

HTH
 
J

Jacob

Jacob said:
If I have this class:

class A {
public A(String a);
}

As pointed out, a better example should be

class A implements B {
public A(String a);
}

Class<A> clazz = (Class<A>) Class.forName("A");
Constructor<A> ctor = clazz.getConstructor(String.class);
B b = ctor.newInstance("hello world");


Though I may understand *why* the compiler complains, I
have a feeling that a warning from the compiler is a strong
hint to modify the code so that the warning disappears.
What use has a warning if it is always there? But I guess
it boils down to the definition of the term "warning", and I
might be influenced by past (and present) experience with
C/C++ compilers as Chris suggests.

But my second question was not adressed. Why doesn't the
compiler complain in this case:

class A implements B {
public A();
}

Class<?> clazz = Class.forName("A");
B b = (B) clazz.newInstance(); // Why is this safe?


Thanks!
 
Ad

Advertisements

C

Chris Uppal

Jacob said:
But my second question was not adressed. Why doesn't the
compiler complain in this case:

class A implements B {
public A();
}

Class<?> clazz = Class.forName("A");
B b = (B) clazz.newInstance(); // Why is this safe?

It seems to be because the compiler doesn't warn (or doesn't warn as often ?)
about casts to interfaces. When you are casting the result of Class.forName()
you are casting to a concrete type, and the compiler whinges about it. It
seems to think that mistakes are less likely with casts to interfaces (why ??).
For instance the following compiles without complaint:

Object o = new A("hello")
B b = (B) o;

Kind of arbitrary... (Which rather nicely illustrates my earlier point ;-)

-- chris
 
T

Thomas Hawtin

Chris said:
It seems to be because the compiler doesn't warn (or doesn't warn as often ?)
about casts to interfaces. When you are casting the result of Class.forName()
you are casting to a concrete type, and the compiler whinges about it. It
seems to think that mistakes are less likely with casts to interfaces (why ??).

I don't think it makes any difference whether the type is a class or an
interface.
For instance the following compiles without complaint:

Object o = new A("hello")
B b = (B) o;

In that case, and the case above, you are casting to a type that can be
checked at runtime.

If you cast to a generic parameter type or a generic type then the cast
cannot be checked at runtime, hence the unchecked cast warning.

Some of the java.util code not only uses code that causes unchecked cast
warnings, but really allows a generic parameter type reference to point
to an incorrect type of object.

Tom Hawtin
 
C

Chris Uppal

Thomas said:
In that case, and the case above, you are casting to a type that can be
checked at runtime.

No I'm not; 'o' is of declared type Object.

-- chris
 
C

Chris Uppal

I said:
No I'm not; 'o' is of declared type Object.

Sorry, I misread you; you said "runtime", not "compile time".

With that correction, I take your point.

I had not, in fact, realised that the compiler doesn't warn about
List<Object> list = ...
A a = (A)list.get(0);

Even more reason to avoid generics ;-)

-- chris
 
Ad

Advertisements

T

Tony Morris

For instance the following compiles without complaint:
Object o = new A("hello")
B b = (B) o;

The misuse/warning of a parameterised type can be roughly interpreted as:
"You opted to use generic type-safety, but circumvented it with a cast - as
a result, I (the compiler) will issue you with a warning". In your above
case, it doesn't hold that you "opted to use generic type-safety",
therefore, you receive no warning.

Of course, you may choose to argue that the premise that I provided above is
somewhat contrived in that it doesn't meet a majority of use cases (or
whatever you might choose), in which case, I offer you the postulation that
you are just "scratching the surface" with respect to the defectiveness of
JSR-14.
 
Ad

Advertisements


Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top