From: Simon Brooke on
OK, here's a problem which many people must have encountered, and there
must be a 'best practice' solution.

I have a thing which is configurable by plugging other things into it.
Obviously the other things I plug in must conform to specific interfaces,
but what thing I actually plug in is determined at run-time by reading
the name of the plugin class from a configuration file. An example (in
Java 1.4) is as follows:

1 String v = config.getValueAsString( "authenticator_class");
2
3 if (v != null) {
4 Class authenticatorClass;
5
6 try {
7 authenticatorClass = Class.forName( v);
8 } catch (ClassNotFoundException c) {
9 throw new InitialisationException(
10 "Could not find class [" + v
11 + "]", c);
12 }
13
14 try {
15 authenticator =
16 (Authenticator) authenticatorClass
17 .newInstance();
18 } catch (ClassCastException e) {
19 throw new InitialisationException(
20 "Not a valid authenticator class", e);
21 } catch (InstantiationException f) {
12 throw new InitialisationException(
23 "Could not instantiate authenticator", f);
24 }
25 }

Obviously one can vacuously 'bring this up to date' by changing line 4 to

4 Class<?> authenticatorClass;

but I feel that the right thing to do must surely be to use

4 Class<Authenticator> authenticatorClass;
5
6 try {
7 authenticatorClass =
(Class<Authenticator>) Class.forName( v);
8 } catch (Exception e) {

then if the class specified did not inherit from Authenticator a
ClassCastException would be caught at line 8, and the second try/catch
block might become redundant. However, if I do that, Java 1.6 gives me a
warning at line 7:

'Type safety: Unchecked cast from Class<capture#1-of ?> to
Class<Authenticator>'

Eclipse offers to fix this by adding an @SuppressWarnings clause, but I'm
not sure I want to suppress warnings...

What is the preferred pattern in Java 1.5/1.6, and why?

--

;; Semper in faecibus sumus, sole profundam variat

From: Simon Brooke on
On Sat, 10 Jul 2010 08:15:52 -0700, markspace wrote:

> Robert Klemme wrote:
>
>
>> This is even better because you don't even need a cast:
>
>> final Class<? extends Authenticator> cl =
>> Class.forName(className).asSubclass(Authenticator.class);
>
>
> Interesting. Although "asSubclass(Class<?>)" is a cast and the
> documentation even says "Casts this Class object to represent a subclass
> of...." It will throw ClassCastException if the named class is not a
> type of Authenticator, which should be caught, imo, because you probably
> want to provide a better error mechanism that just halting.
>
> Definitely more than one way to do it, I agree.

No, but that solution definitely looks interesting and tidy. I wasn't
aware of the asSubclass() refinement, that's useful.

The solution I've ended up with is as follows:

1 String authClassName = config
2 .getValueAsString( AUTHENTICATORCLASSMAGICTOKEN);
3
4 if (authClassName != null) {
5 try {
6 this.authenticator = Class.forName( authClassName).asSubclass(
7 Authenticator.class).newInstance();
8 } catch (ClassCastException cce) {
9 throw new InitialisationException(
10 "Specified class 'v' was not an authenticator".replace(
11 "v", authClassName), cce);
12 } catch (ClassNotFoundException cnfe) {
13 throw new InitialisationException(
14 "Specified class 'v' was not found".replace( "v",
15 authClassName), cnfe);
16 } catch (InstantiationException ie) {
17 throw new InitialisationException(
18 "Could not create an instance of " + authClassName, ie);
19 } catch (IllegalAccessException iae) {
20 throw new InitialisationException( "Operation not permitted",
21 iae);
22 }
23 }
24
25 authenticator.init( config);

This still makes the code quite hard to read because there are so many
different exceptions being thrown; while I agree with the general
statement that one should only seek to catch the exceptions once expects
to occur (because unexpected exceptions ought to give rise to some
further escalation) given that all I'm going to do with any of these
exceptions is log it, I can't help wishing they were all subclasses of
some common 'WhileInstantiatingClassException' superclass which I could
catch just once!

--

;; Semper in faecibus sumus, sole profundam variat