Generics: instantiating an object from a class name inconfiguration

S

Simon Brooke

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?
 
A

Arne Vajhøj

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?

Have you tried:

authenticatorClass = Class<Authenticator>.forName( v);

?

Arne
 
L

Lew

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

4 Class<?> authenticatorClass;

Do not use TAB characters to indent Usenet posts; use a maximum of four spaces
per level for readability.
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) {

Don't catch 'Exception', catch specific exceptions.
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?

Excellent question, fully answered in the free chapter on generics
downloadable from
<http://java.sun.com/docs/books/effective/>
although of course you should study the entire book.

The gist is that sometimes you have to suppress warnings, but only when you
know for sure that it's safe to do so and document why it's safe with comments
in the code. So what you want is similar to:

public Authenticator instantiate( String name )
throws InitialisationException
{
Authenticator authenticator;
try
{
// ClassCastException caught so this is safe
@SuppressWarnings( "unchecked" )
Class <Authenticator> clazz =
(Class <Authenticator>) Class.forName( name );

authenticator = clazz.newInstance();
}
catch ( ClassNotFoundException ex )
{
String msg = "Could not find class \""+ name +"\". ";
logger.error( msg + ex.getLocalizedMessage(), ex );
throw new InitialisationException( msg, ex );
}
catch ( ClassCastException ex )
{
String msg = "Not a valid authenticator class \""+ name +"\". ";
logger.error( msg + ex.getLocalizedMessage(), ex );
throw new InitialisationException( msg, ex );
}
catch ( IllegalAccessException ex )
{
String msg =
"Could not access instance of \""+ name +"\". ";
logger.error( msg + ex.getLocalizedMessage(), ex );
throw new InitialisationException( msg, ex );
}
catch ( InstantiationException ex )
{
String msg =
"Could not instantiate authenticator from \""+ name +"\". ";
logger.error( msg + ex.getLocalizedMessage(), ex );
throw new InitialisationException( msg, ex );
}
assert authenticator != null;
return authenticator;
}

I usually use some sort of FubarMessage enum with "friendly" strings invoked
via 'toString()' to embody the fubar messages. You could even have a custom
'public String message( String name )' method in the enum to build each 'msg'.
 
M

markspace

Lew said:
try
{
// ClassCastException caught so this is safe
@SuppressWarnings( "unchecked" )
Class <Authenticator> clazz =
(Class <Authenticator>) Class.forName( name );

authenticator = clazz.newInstance();
}


The following doesn't generate any warning messages and doesn't require
a @SuppressWarnings annotation. It's also a tad shorter. It will throw
ClassCastException if the type loaded isn't right, but that could be
caught too. I'm not sure it's better, but it is an alternative.

try
{
Class<?> c = Class.forName( "test" );
SomeType s = (SomeType) c.newInstance();

} catch( InstantiationException ex ) { ... // etc.
 
A

Arne Vajhøj

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?

Have you tried:

authenticatorClass = Class<Authenticator>.forName( v);

?

I just did myself.

It does not compile.

:-(

Arne
 
R

Roedy Green

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?

here is how I handle that:

// Make sure the class we dynamically load implements the
// Configuration interface:
final Class<? extends Configuration> configurationClass =
Class.forName( binaryClassName ).asSubclass( Configuration.class );

configuration = configurationClass.newInstance();
 
R

Robert Klemme

The following doesn't generate any warning messages and doesn't require
a @SuppressWarnings annotation. It's also a tad shorter. It will throw
ClassCastException if the type loaded isn't right, but that could be
caught too. I'm not sure it's better, but it is an alternative.

try
{
Class<?> c = Class.forName( "test" );
SomeType s = (SomeType) c.newInstance();

} catch( InstantiationException ex ) { ... // etc.

This is even better because you don't even need a cast:

public static Authenticator create1(String className) throws
CreationException {
try {
final Class<? extends Authenticator> cl =
Class.forName(className).asSubclass(Authenticator.class);
return cl.newInstance();
} catch (ClassNotFoundException e) {
throw new CreationException("Class not found: " + className, e);
} catch (InstantiationException e) {
throw new CreationException("Could not create instance: " +
className, e);
} catch (IllegalAccessException e) {
throw new CreationException("Operation not permitted", e);
}
}

You can even pack it in one line and it doesn't look too ugly:

public static Authenticator create2(String className) throws
CreationException {
try {
return
Class.forName(className).asSubclass(Authenticator.class).newInstance();
} catch (ClassNotFoundException e) {
throw new CreationException("Class not found: " + className, e);
} catch (InstantiationException e) {
throw new CreationException("Could not create instance: " +
className, e);
} catch (IllegalAccessException e) {
throw new CreationException("Operation not permitted", e);
}
}

Kind regards

robert
 
M

markspace

Robert said:
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.
 
S

Simon Brooke

Robert Klemme wrote:





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!
 
R

Robert Klemme

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!

From the point of modularity I would definitively refactor the creation
code out into a separate method - even if it's just a private one. This
helps keep your other method much more readable.

Btw, what's the value of "authenticator" if the config does not contain
this entry? Is there some default value? In that case I'd structure
the code rather as

final String authClassName = config.getValueAsString(
AUTHENTICATORCLASSMAGICTOKEN);
authenticator = authClassName == null ? createDefaultAuth() :
createAuth(authClassName);
authenticator.init( config );

If this is in the constructor you can even make "authenticator" final
(if it is supposed to never change of course).

Kind regards

robert
 
S

Simon Brooke

From the point of modularity I would definitively refactor the creation
code out into a separate method - even if it's just a private one. This
helps keep your other method much more readable.

Fair point. Indeed, as I do exactly similar things with other types of
pluggable modules, it might be better to have a method

Btw, what's the value of "authenticator" if the config does not contain
this entry? Is there some default value?

Defaults to JDBCAuthenticator, because in the general case I authenticate
against first class database users (actual database user accounts); but I
can plug in authenticators which authenticate against a particular
database table or against OpenId, for example.
In that case I'd structure
the code rather as

final String authClassName = config.getValueAsString(
AUTHENTICATORCLASSMAGICTOKEN);
authenticator = authClassName == null ? createDefaultAuth() :
createAuth(authClassName);
authenticator.init( config );

If this is in the constructor you can even make "authenticator" final
(if it is supposed to never change of course).

Again, good point, thanks. It's not (typically) in a constructor because
most of my stuff extends Servlets where the constructor does not have
access to the configuration; but it is typically in the init() method so
it's pretty much the same thing.
 
D

Daniel Pitts

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?

Have you tried:

authenticatorClass = Class<Authenticator>.forName( v);

?

I just did myself.

It does not compile.

forName returns a Class<?> only, you can not ask it to do otherwise.
You might be able to do:
Class.forName(v).asSubclass(Authenticator.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
473,769
Messages
2,569,582
Members
45,070
Latest member
BiogenixGummies

Latest Threads

Top