Re: passing a Factory to a method to create a generic instance
On Sat, 10 May 2008, Mark Space wrote:
Tom Anderson wrote:
How about doing the assignmentLoad in the constructor? That would mean you
have to mass the ios into the constructor, which means using some more
complicated reflection to invoke it, rather than newInstance. I think like
this:
Class<AssignmentLoadable> cl = determineObjectClass() ;
AssignmentLoadable a = cl.getConstructor(IOStream.class).newInstance(ios) ;
So why not use the constructor:
For me, there are two issues. First, a class may have more than one
constructor, and it can be difficult to know, based on just data input,
exactly which constructor should be called.
I'm thinking there would be a defined constructor signature that would be
used (as implied in my code). There wouldn't be any need to choose between
options.
Second, constructors are not inherited and can't be members of an
interface. Thus, like statics, they're also inconvenient to use. You
can't be assured that a sub-class of another class implements all the
same signatures that the super class does (because they aren't
inherited).
That's true, but ...
AssignementLoadable otoh is guaranteed to exist, because it's a public
member of the super class. The sub-class must have the assignmentLoad
method, either by overloading it or by inheritance.
Using just the default constructor makes it easy to know which
constructor to call: it's always the default one.
.... if a class doesn't have a no-args constructor ...
If a class doesn't have a default constructor, then I can't instantiate
it with this routine, of course, but that's as close as I can't get to a
guaranteed constructor.
.... exactly. You have to make a leap of faith that the constructor you
want is there, just as i have to. Just because your leap of faith doesn't
take any parameters doesn't make it any more certain!
As an aside, does the introduction of generics, and the way they're used
by Class, mean that something along the lines of a constructor interface
would be useful? Before generics, even if had been was a way to require
that all implementations of an interface declared a specified constructor,
there was no way to make use of it - you call a constructor using an
explicit type literal, where polymorphism doesn't come into play, or via
reflection, where there's no type safety anyway. Can we use generics to
make this possible? Say we could declare constructors in interfaces:
interface AssignmentLoadable {
public AssignmentLoadable(IOStream ios) ;
}
And have them enforced in implementations, can we do something like:
<T implements AssignmentLoadable> T create(Class<T> cl, IOStream ios) {
// somehow invoke the constructor! like one of:
// looks like an anonymous class declaration
return new AssignmentLoadable(ios) cl ;
// pretend new is a static method:
return AssignmentLoadable.new(cl, ios) ;
// python style - single namespace for vars and types
return new cl(ios) ;
}
These syntagma are all pretty horrible. But does the idea work?
At they bytecode level, no; the 'new' bytecode can only reference classes
from the constant pool, not the stack. You'd need a new 'dynamicnew' or
some such bytecode that does so. Without that, this code would have to be
expanded into some reflective calls.
Then a series of getters and setters will configure the class. While
there's other ways to do this (Serializable, for example), assuming
setters seems the easiest way of reading a class from a arbitrary
format. You just assume each data item has a corresponding setter, a
simple 1-to-1 mapping.
Hang on, i thought you were configuring the class through
AssignmentLoadable.load? This sounds like you've gone reflective.
I don't feel there's any danger here that's different that using a Java
bean, which is basically initialized and created the same way.
Yes. Java Beans are also unholy!
The object is correctly created by it's constructor.
Except it isn't, because the constructor doesn't have the information it
needs to create the object. Yes, it allocates memory for it and clears its
fields and runs the instance initialiser, but that's only creation in a
trivial sense. If i do Room.class.newInstance(), i have a Room object, but
i don't have a room - it doesn't have a number, a floor, or a number of
beds.
I know that in correct use, you'll go straight from instantiation to
initialisation, so the period of Room-that-is-not-a-room is vanishingly
small, and no danger can occur. The problem arises when the use is
incorrect, and uninitialised Rooms can escape to unwitting code. In our
discussion of operator overloading, you said "I like to design things that
can't fail" - where failure is completely impossible by design. That's
what i'm trying to get at here.
tom
--
I know you wanna try and get away, but it's the hardest thing you'll
ever know