From: Arne Vajhøj on
On 09-07-2010 19:07, Simon Brooke wrote:
> 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

From: Lew on
Simon Brooke wrote:
> 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'.

--
Lew
From: markspace on
Lew wrote:

> 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.

From: Arne Vajhøj on
On 09-07-2010 20:13, Arne Vajhøj wrote:
> On 09-07-2010 19:07, Simon Brooke wrote:
>> 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
From: Roedy Green on
On 9 Jul 2010 23:07:32 GMT, Simon Brooke
<stillyet+nntp(a)googlemail.com> wrote, quoted or indirectly quoted
someone who said :

>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();
--
Roedy Green Canadian Mind Products
http://mindprod.com

You encapsulate not just to save typing, but more importantly, to make it easy and safe to change the code later, since you then need change the logic in only one place. Without it, you might fail to change the logic in all the places it occurs.