Raymond said:
Chris Uppal wrote: [...]
The meaning of an undecorated List has not changed (as you say)
between 1.4 and
1.5. In both cases it is just a List with no additional meta-data.
You would
not have been justified in assuming that any actual instance of List
in a 1.4
program could correctly have instances of Point added to it. In 1.4
you didn't
have a way of /expressing/ that (in Java, as opposed to
documentation), but it
was still true. In 1.5 exactly the same thing applies -- you don't know
(without knowledge additional to the Java API spec) what kind of
objects it is
safe to add to an unadorned List. However, if you have been given a
List<Object> then you /do/ know exactly what is safe to add --
anything. The
two concepts are not equivalent at all.
That is only true if you have casts from List<Foo> to List in the code.
My contention is that such a cast is bad form. A List<Foo> would only
ever be casted to List<?> and it's variants.
I don't see how casting comes into the picture at all. Generics are
about extending the Java type system with additional metadata. Casting
does affect the declared type and type metadata of an object reference,
but generics are a lower-level concept: they apply to the types cast
from and to, which you must have before -- and irrespective of -- any
consideration of casting.
To the extent that one of the goals of generics seems to be to as much
as possible avoid any need to cast object references, you have stated a
casting guideline quite in line with the Generics spirit. List<?> is a
supertype of List<Foo> for any Foo, so you never have to actually write
such a cast, and if you do then you can be certain that it is correct.
<aside>
It is perhaps a quirk of Generics that *both* List<? super Foo> and
List<? extends Foo> are also supertypes of List<Foo>, along with
List<?>. At the same time, List<FooSubclass> nor List<FooSuperclass>
are both _siblings_ of List<Foo>; there are no sub-/supertype
relationships among them.
(The only potential flaw I
see in this convention is that I do not have enough experience with
generics to know if it is possible to practically enforce such a
restriction.)
As I point out, there are other bona fide supertypes of List<Foo> that
it is always safe to cast to. It is never necessary to cast to any of
these, though, any more than any other upcast is ever necessary. The
compiler will produce warnings about other casts, which may meet your
"enforcement" criterion.
You did not address my first point, that considering things in this
manner preserves the meaning of List from 1.4.
I think he did, which tells me that there continues to be some kind of a
disconnect here.
[...]
My point is that the existence of generics breaks OO principles. For
example, given classes Foo and Bar, under an OO system is one can cast a
Foo object to a Bar object, then it is safe to assume that any method on
Bar is safely and correctly implemented in Foo.
Sun already wrote this rule once with the unmodifiable versions of the
Collection API.
You mean "broke", maybe? I'll agree that the concept of "optional
operations" in the Collections API is an OO blunder, and I think you'll
find others here who agree.
It has done it again with generics in being able to
cast List<String> to List. The second example is more egregious because
it can lead to subtler bugs.
I disagree here. You are missing the point that List is *not* a
supertype of List<Foo>. Neither is List<Object>. It is natural, then,
that casting a List<Foo> to raw List can produce subtle bugs, and the
compiler helpfully warns you about such situations. As I note above,
the compiler issues warnings for these situations. It is consistent
with the compiler's historic behavior that it allows any cast that
*could* be OK at runtime, and assumes that the programmer knows what he
is doing.
Or perhaps your point *is* that List is not a supertype of List<Foo>?
That is perhaps a valid complaint about the type system as extended in
1.5, but it is hardly an OO issue.