Quite complicated, and still not perfect, because it
is not safe for multithreading or recursion (due to
the use of static fields):
class Customer
{ public Customer
( final java.lang.String first, final java.lang.String last )
{} }
class SlashSeparatedCustomer extends Customer
{
private static java.lang.String first;
private static java.lang.String first( final java.lang.String name )
{ if( !initialized )initialize( name ); return first; }
private static java.lang.String last;
private static java.lang.String last( final java.lang.String name )
{ if( !initialized )initialize( name ); return last; }
private static boolean initialized;
private static void initialize( final java.lang.String name )
{ if( !initialized )
{ final int slash = name.indexOf( '/' ) ;
first = name.substring( 0, slash );
last = name.substring( slash + 1 ); }}
public SlashSeparatedCustomer( final java.lang.String name )
{ super( first( name ), last( name )); }}
Yep, that's pretty evil too.
You could make it threadsafe by using a
java.util.concurrent.locks.ReentrantLock, which you would acquire in
initialize, and then release after the super call. You could also clear
the cache variables there too. Like this:
private static final Lock initLock = new ReentrantLock() ;
private static void initialize(String name) {
if (!initialized) {
initLock.lock() ;
int slash = name.indexOf( '/' ) ;
first = name.substring( 0, slash );
last = name.substring( slash + 1 );
}
}
public SlashSeparatedCustomer(String name) {
super(first(name), last(name)) ;
postinitialize() ;
}
private void postinitialize() {
first = null ;
last = null ;
initLock.unlock() ;
}
Making it recursable would be harder. You could factor the cache variables
into a class, say CustomerParameters, and maintain a stack of them, so
that if initialize was called a second time (detected by the lock being
acquired but the cache fields already being set), you push the current
cache values onto the stack and carry on; you would then pop the stack in
postinitialize.
Although at that point, the Right Solution here becomes clear: you define
the CustomerParameters class in Customer (as a protected static class, i
think - is that even possible?), then give Customer a constructor which
takes one, and uses it to set its fields. You can then put all your
complex before-super logic in a static method which returns a
CustomerParameters object:
class Customer {
protected static class Parameters {
public final String firstName ;
public final String lastName ;
public Parameters(String firstName, String lastName) {
this.firstName = firstName ;
this.lastName = lastName ;
}
}
private final String firstName ;
private final String lastName ;
public Customer(String firstName, String lastName) {
this.firstName = firstName ;
this.lastName = lastName ;
}
protected Customer(Parameters params) {
this(params.firstName, params.lastName) ;
}
}
class SlashSeparatedCustomer extends Customer {
private static Customer.Parameters parse(String name) {
int slash = name.indexOf('/') ;
String first = name.substring(0, slash) ;
String last = name.substring(slash + 1) ;
return new Customer.Params(first, last) ;
}
public SlashSeparatedCustomer(String name) {
super(parse(name)) ;
}
}
The underlying trick here is binding together all the things you want in
an object, and then - and this is really the heart of it - using a method
call to effectively assign that to a local variable, from which you can
then read out fields. The second call creates a method parameter, which is
basically like a local variable, and it does it in a situation where
creating local variables isn't allowed. Does that make sense?
And in fact, once you see it like that, you can push this trick down
entirely to the subclass level (and simplify a bit):
class Customer {
private final String firstName ;
private final String lastName ;
public Customer(String firstName, String lastName) {
this.firstName = firstName ;
this.lastName = lastName ;
}
}
class SlashSeparatedCustomer extends Customer {
private static class Parameters {
public final String firstName ;
public final String lastName ;
public Parameters(String name) {
int slash = name.indexOf('/') ;
firstName = name.substring(0, slash) ;
lastName = name.substring(slash + 1) ;
}
}
private SlashSeparatedCustomer(Parameters params) {
super(params.firstName, params.lastName) ;
}
public SlashSeparatedCustomer(String name) {
this(new Parameters(name)) ;
}
}
Maybe even better:
class SlashSeparatedCustomer extends Customer {
private static String[] parse(String name) {
int slash = name.indexOf('/') ;
firstName = name.substring(0, slash) ;
lastName = name.substring(slash + 1) ;
return new String[] {firstName, lastName} ;
}
private SlashSeparatedCustomer(String[] params) {
super(params[0], params[1]) ;
}
public SlashSeparatedCustomer(String name) {
this(parse(name)) ;
}
}
Yes, this uses an array to hold multiple return values, which is a bit
icky, and won't work if the parameters are of different types (at least,
not without casts, and then it's *really* icky), but it saves having to
create a class just for this workaround.
And that is my final word on the matter.
For today.
tom