How should I re-write this?

F

Fencer

Hello, consider the following program:

package action;

public class MyTest {

public static void main(String[] args) {
try {
ClassLoader cl = MyTest.class.getClassLoader();
Class<?> c = cl.loadClass("action.SomeClass");
SomeClass inst = (SomeClass)c.newInstance();
System.out.println(inst);
SomeClass inst2 = MyTest.loadClass("action.SomeClass");
System.out.println(inst2);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}

public static <T> T loadClass(String className) {
T inst = null;
try {
ClassLoader cl = MyTest.class.getClassLoader();
Class<?> c = cl.loadClass(className);
inst = (T)c.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

return inst;
}
}

class SomeClass {
@Override public String toString() {
return "1337";
}
}

I'm getting a warning on line 27 for the following statement:
inst = (T)c.newInstance();
The warning reads:
Type safety: Unchecked cast from capture#4-of ? to T

Ok, I think I get it. The compiler cannot know at compile time that the
cast will work. But if you look at the code in the main method, I don't
get a warning on line 11:
SomeClass inst2 = MyTest.loadClass("action.SomeClass");
I thought the above line has the same problem as the one that is being
warned about?

I have two questions:
1. Why don't I get a warning for line 11
SomeClass inst2 = MyTest.loadClass("action.SomeClass"); ?

2. How should I rewrite the generic method loadClass?

- Fencer
 
S

Stefan Ram

Fencer said:
I have two questions:
1. Why don't I get a warning for line 11
SomeClass inst2 = MyTest.loadClass("action.SomeClass"); ?

Guess: The compiler seems to infer the type T from the call
site, that is T=SomeClass.
2. How should I rewrite the generic method loadClass?

If everything else fails, I use

@java.lang.SuppressWarnings( "unchecked" )

.
 
M

markspace

....your only requirement on the loaded class is that it has the
toString() method on it, as specified by Object, so you probably ought
to write:

Object inst2 = MyTest.loadClass(Object.class, "action.SomeClass");

So you're no longer referring to action.SomeClass statically.


I think that the problem is expecting generics to do anything here.
Given that the OP is trying to get a "known class" out of a string, how
can he possibly do that? I think the should just admit that he can't
and make the caller deal with it.

public static Object loadClass( String className ) { ...

is how I would declare that method. It's basically the same as the
method Java provides on the Class object and, well, there's a reason for
that.

A more useful idea might be to assume that you have some type, some
interface, that your string-named class is going to implement. Then you
can at least check that the returned class implements that interface,
even if you don't know its exact type at runtime. So here's another
idea for "loadClass," which I've renamed here to make the example be a
bit more clear:

public static java.sql.Driver loadDB( String className ) {
T inst = null;
try {
ClassLoader cl = MyTest.class.getClassLoader();
Class<?> c = cl.loadClass(className);
inst = (java.sql.Driver)c.newInstance();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

return inst;
}
}
 
L

Lew

Stefan said:
  If everything else fails, I use

@java.lang.SuppressWarnings( "unchecked" )

Note that if one's analysis is incorrect, i.e., that the types don't
actually match, then the suppression of warnings defeats the purpose
of generics, to eliminate ClassCastException.

Suppressing the "unchecked" warning is reserved for times when the
programmer has guaranteed that there cannot be a ClassCastException,
but cannot satisfy the compiler that this is so, e.g., when using an
expression like casting '(T)'
to a parametrized type.

If the programmer has not made that guarantee, then there's no point
to suppressing the warning.
 
L

Lew

Fencer said:
Hello, consider the following program:

package action;

public class MyTest {

    public static void main(String[] args) {
       try {
          ClassLoader cl = MyTest.class.getClassLoader();
          Class<?> c = cl.loadClass("action.SomeClass");
          SomeClass inst = (SomeClass)c.newInstance();
          System.out.println(inst);
          SomeClass inst2 = MyTest.loadClass("action.SomeClass");
          System.out.println(inst2);
       } catch (ClassNotFoundException e) {
          e.printStackTrace();
       } catch (InstantiationException e) {
          e.printStackTrace();
       } catch (IllegalAccessException e) {
          e.printStackTrace();
       }
    }

    public static <T> T loadClass(String className) {
       T inst = null;
       try {
          ClassLoader cl = MyTest.class.getClassLoader();
          Class<?> c = cl.loadClass(className);
          inst = (T)c.newInstance();
       } catch (ClassNotFoundException e) {
          e.printStackTrace();
       } catch (InstantiationException e) {
          e.printStackTrace();
       } catch (IllegalAccessException e) {
          e.printStackTrace();
       }

       return inst;
    }

}

class SomeClass {
    @Override public String toString() {
       return "1337";
    }

}

I'm getting a warning on line 27 for the following statement:
inst = (T)c.newInstance();
The warning reads:
Type safety: Unchecked cast from capture#4-of ? to T

Ok, I think I get it. The compiler cannot know at compile time that the
cast will work. But if you look at the code in the main method, I don't
get a warning on line 11:
SomeClass inst2 = MyTest.loadClass("action.SomeClass");
I thought the above line has the same problem as the one that is being
warned about?

No, because here you are (unsafely) telling the compiler what the 'T'
of the generic method is.
I have two questions:
1. Why don't I get a warning for line 11
SomeClass inst2 = MyTest.loadClass("action.SomeClass"); ?

Because the compiler infers 'T' from the declaration.
'ClassLoader#loadClass()' is not generically parametrized so cannot do
that.
2. How should I rewrite the generic method loadClass?

Well, you really don't give the method enough information to prevent a
'ClassCastException', so the generic parameter 'T' is essentially a
lie. I'm not sure that you can reliably generify it without catching
the 'ClassCastException'.
 
W

Wojtek

Lew wrote :
Note that if one's analysis is incorrect, i.e., that the types don't
actually match, then the suppression of warnings defeats the purpose
of generics, to eliminate ClassCastException.

Suppressing the "unchecked" warning is reserved for times when the
programmer has guaranteed that there cannot be a ClassCastException,
but cannot satisfy the compiler that this is so, e.g., when using an
expression like casting '(T)'
to a parametrized type.

If the programmer has not made that guarantee, then there's no point
to suppressing the warning.

Moreover the suppress warnings acts on the entire method, so any and
all questionable casts will be ignored.

The only place I use SuppressWarnings( "unchecked" ) is in a method
which only does that one cast to return the object.
 
L

Lew

Stefan said:
Moreover the suppress warnings acts on the entire method, so any and
all questionable casts will be ignored.

Only if you apply it to the entire method. You should probably not do
that, but apply it to a narrower scope.
The only place I use SuppressWarnings( "unchecked" ) is in a method
which only does that one cast to return the object.

Apply the annotation to the narrowest scope feasible. I assign a
variable to absorb the uncheckedness.

public <T> T makeT( String specifiesT )
{
@SuppressWarnings("unchecked") // the String arg will prevent
ClassCastException
final T foo = (T) Class.forName( specifiesT );
return foo;
}

This snippet does not properly show how the code guarantees the
correct type, so actually it's wrong. If I were doing this correctly,
there'd be code to ensure that the '@SuppressWarnings()' actually is
safe, and the comment would explain that, consistent with the
recommendations in /Effective Java/ by Joshua Bloch.

<http://java.sun.com/docs/books/effective/generics.pdf>
 
L

Lew

Steven said:
Note that you've already statically referred to action.SomeClass, so
it's already loaded,

but not initialized, at least not by the reference to the class.
 

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

Members online

Forum statistics

Threads
474,431
Messages
2,571,679
Members
48,796
Latest member
Greg L.

Latest Threads

Top