Re: The daily WTF: interface Addin?
Andreas Leitgeb wrote:
I agree to both aspects of this paragraph. :-)
(limited to the information in the technical part, your
answer was correct, and the mentioned remark (that the
technical part was not enough) should have been added.)
The technical part being "public void initialize();", I'm not sure I
agree. It's fairly clear that for whatever reason implementors need to
be initializable separately from construction. Perhaps a resource that
shouldn't be greedily acquired, like a lock or file handle, is needed,
or one that doesn't sensibly survive things like RMI or serialization.
Of course, to know exactly what needs to be initialized requires reading
the javadocs.
I'm sure the compiler would consider the technical part to be "enough" :)
As for marker intervaces vs. annotations, I've got a tricky one for you.
Suppose you make a fixed-length vector class -- it's supposed to be an
N-dimensional point in space or whatever, unchanging in size after
construction. Operations with the vectors and with matrices need
compatible sizes. It seems there are now three ways to go about it.
Method 1 is the obvious method: thinly wrap a java.util.Vector<Foo> and
have your operations maintain their sizes as invariant after
constructing with a given number of dimensions and throw exceptions when
different sizes are mixed.
Method 2 would require generics and would be to create a MathVec<Dim>
interface or class specifying methods that take and return
MathVec<Dim>s, and MathMatrix<Dim1, Dim2> with operations like
MathVec<Dim1> multiplyBy (MathVec<Dim2>) or <Dim3> MathMatrix<Dim1,
Dim3> multiplyBy (MathMatrix<Dim2, Dim3>. You'd create marker interfaces
like TwoD and ThreeD and work with MathVec<TwoD>s and MathVec<ThreeD>s;
mismatching them would get flagged at compile time. Clearly an
improvement, but you might also want to include the checks and
exceptions in case the classes are used nongenerically, or just in case.
Method 3 would be to replace the Method 2 marker interfaces with
annotations, but then there's two huge questions. It's easy to make
something like this:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.LOCAL_VARIABLE,ElementType.METHOD,ElementType.PARAMETER})
public @interface Dimensions {
int value();
}
and slap @Dimensions(2) before every declaration of a MathVec that's
supposed to be 2D, but then:
* How do you check, at run time, the dimensions of things like argument
and formal parameter match up? Probably the old-fashioned way of
comparing the sizes of the java.util.Vectors and throwing exceptions still.
* How do you get compile-time checking?
I'm thinking that for this example, marker interfaces work better. It
puts the dimension information into the type, which is where it belongs
anyway, IMO.
Is there a more general theory on when to use marker interfaces and when
to use annotations? Or is "use a marker interface when changing it
should mean you're changing the type of a value, and annotations
otherwise" it? Clearly mathematical vectors of different lengths that
can't interoperate are logically separate value types, for instance.
OTOH, this would suggest that Serializable and Cloneable belong as
annotations, since making something serializable isn't really changing
its type. It would also suggest that some of the java.util classes,
ironically including Vector, are poorly implemented. All of the
collection interfaces would be better off duplicated with an
UnmodifiableFoo to go with each Foo; or else ModifiableFoo interfaces
with the "optional operations" created. So instead of Set with its
specified methods, you'd have Set with the methods that don't modify the
set and ModifiableSet extends Set with the methods that do (or
UnmodifiableSet and Set extends UnmodifiableSet, but that's uglier since
it makes the implicit claim that a modifiable set is a "kind of"
unmodifiable set...much less reasonable than that a modifiable set is
merely a kind of set). Then again, these aren't marker interfaces then,
since the extended interface would add methods. Of course we'd have to
add ModifyingIterator too, extending a slightly smaller Iterator
interface and adding the remove() method removed from the former. (And
why not throw in a new "for" syntax while we're at it? Inside a for
(Type obj : coll) { things } loop allow the construction
"obj.for.remove()" that removes obj? With ModifiyingIterator this could
be checked at compile time. It should then be idempotent -- multiple
remove()s on the same item remove it once, rather than throw an exception.)