Method chaining with generics

C

Chris Uppal

Raymond said:
Let's boil this argument down a bit. The crux of the matter seems to be
how we are viewing a List. My viewpoint is that a List is equivalent to
List<Object>. They are both "lists of Objects". A List<?> is a "list
of a specific unknown thing."

The viewpoint of Oliver and John appears to be that a List is not
necessarily the same as List<Object>; a List is a "list of a specific
unknown thing" and List<Object> is a "list of objects". List<?> is a
"list of a specific unknown thing."

My impression is that your summary of the participants' positions is correct.

However (and please don't take this personally!) I'm also sure that it is you
who are wrong in this case.

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.


BTW, just as an aside:
Finally, let me say that this whole discussion really shows how Sun
screwed up generics from an OO perspective. It is reminiscent of the
modifiable/unmodifiable collections disaster.

Although I agree that the new generics are a complete mess, this doesn't really
have anything at all to do with OO. Java's generics (or, if you prefer, what
Java's generics would probably have been if they'd been done "right") are
(only) about static declarative type checking -- which is completely orthogonal
to OO.

-- chris
 
R

Raymond DeCampo

Chris said:
Raymond DeCampo wrote:




My impression is that your summary of the participants' positions is correct.

However (and please don't take this personally!) I'm also sure that it is you
who are wrong in this case.

No offense taken. I think this may be more of a case of style than
right or wrong however. Taking either tact allows you to write code
that does the right thing.
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. (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.)

You did not address my first point, that considering things in this
manner preserves the meaning of List from 1.4.
BTW, just as an aside:




Although I agree that the new generics are a complete mess, this doesn't really
have anything at all to do with OO. Java's generics (or, if you prefer, what
Java's generics would probably have been if they'd been done "right") are
(only) about static declarative type checking -- which is completely orthogonal
to OO.

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

Ray
 
J

John C. Bollinger

Chris said:
Raymond DeCampo wrote:

More or less. My contention is that the *use* of an unadorned List is
almost always as a "list of specific unknown thing", even though the API
and runtime allow you to treat one as a "list of any object". If you
allow that the latter is a special case of the former then I'd
completely agree with your characterization of my position.

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

Thanks, Chris, that's just what I was trying to express.
 
J

John C. Bollinger

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

Raymond DeCampo

John said:
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.

I think that we are using the word "cast" in two different ways. You
appear to be using it to imply that an explicit cast operation in code
is involved. I am using it in the more general sense of transforming a
reference from one type to another. I apologize if this has caused
confusion. I am not aware of another word to convey this meaning.
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.



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.

Please interpret "cast" as indicated above.
I think he did, which tells me that there continues to be some kind of a
disconnect here.

Apparently, but this has not helped me understand what it is. :)
[...]
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.

Yes, I meant broke. Sometimes I don't know what my fingers are thinking....
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.

Well, this whole subthread started because I contended that the compiler
should issue warnings when casting from List<Foo> to List and not
necessarily when casting from List to List<Object>. Oliver Wong was the
main other participant at that point. You jumped in when we were
discussing the different conceptual views of List, List<Object> and List<?>.

I'm not sure to what you are disagreeing with when you say "I disagree
here." Are you disagreeing with the suggestion that generics has
(further) screwed up the OO-ness of Java?

Assuming you are, in a true OO system, one would not have an allowable
cast (or assignment) from a type to a type that is not either a super-
or subclass. By allowing this, Sun has further damaged the OO-ness of Java.
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.

Ray
 
H

Hemal Pandya

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. 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 agree that Sun doesn't care too much for Liskov Substitution
Principle. Case in point is Stack, which extends Vector, which breaks
the fundamental guarantee a Stack needs to provide, that inserts and
deletes occur only at the top (or bottom, depending on which way you
look at it). Also the very existence of UnsupportedOperationException.

But I don't see how the existence of generics breaks that.
 
R

Raymond DeCampo

Hemal said:
I agree that Sun doesn't care too much for Liskov Substitution
Principle. Case in point is Stack, which extends Vector, which breaks
the fundamental guarantee a Stack needs to provide, that inserts and
deletes occur only at the top (or bottom, depending on which way you
look at it). Also the very existence of UnsupportedOperationException.

But I don't see how the existence of generics breaks that.

A List<String> is supposed to only allow adding String objects. Casting
it to a List removes this protection. Whether or not you think this is
a violation of OO principles, it isn't a good situation.

Ray
 
H

Hemal Pandya

Hemal said:
My point is that the existence of generics breaks OO principles.
[snip]


I agree that Sun doesn't care too much for Liskov Substitution
Principle. Case in point is Stack, which extends Vector, which breaks
the fundamental guarantee a Stack needs to provide, that inserts and
deletes occur only at the top (or bottom, depending on which way you
look at it). Also the very existence of UnsupportedOperationException.

But I don't see how the existence of generics breaks that.

A List<String> is supposed to only allow adding String objects. Casting
it to a List removes this protection. Whether or not you think this is
a violation of OO principles, it isn't a good situation.

Ok, I see you are talking about generics the way Sun implemented them.
But perhaps a more robust (less error-prone) model could have been
developed.

Given that List already existed and List<T> had to be introduced, IMHO
the best protection would be to give a warning when List<String> is
cast to List. I am in total agreement with you on this.
 
J

John C. Bollinger

Raymond said:
I think that we are using the word "cast" in two different ways. You
appear to be using it to imply that an explicit cast operation in code
is involved. I am using it in the more general sense of transforming a
reference from one type to another. I apologize if this has caused
confusion. I am not aware of another word to convey this meaning.

The JLS uses the word "conversion", as in "assignment conversion",
"method invocation conversion", "casting conversion", etc.. This may be
what you are looking for. Each kind of conversion described in the JLS
has slightly different rules from the others relative to type
compatibility. The word "cast" does have a meaning, to me at least,
that is indeed rather narrower than the general concept of type
conversion. I suspect that you will find many others around here who
will assume that definition, but you may also find others who, like
yourself, use it in a less precise sense.
 
J

John C. Bollinger

Raymond said:
A List<String> is supposed to only allow adding String objects. Casting
it to a List removes this protection. Whether or not you think this is
a violation of OO principles, it isn't a good situation.

Casting in general is a problem for a type system. There are many ways
that casting can cause problems quite apart from any consideration of
generic types, thus avoiding casting as much as possible was apparently
one of the goals of the generics design team. As measured against that
goal, I think they did quite well.

Allowing generic types to be cast to raw types and to other generic
types with different, incompatible, type parameters are, I think,
concessions to binary compatibility between 1.5 classes and earlier
classes, combined with implications of the language specification that
run much deeper than generics. Whether or not the compatibility goal
was appropriate is open to debate, but it was a core principle for the
generics design.
 
R

Raymond DeCampo

John said:
The JLS uses the word "conversion", as in "assignment conversion",
"method invocation conversion", "casting conversion", etc.. This may be
what you are looking for. Each kind of conversion described in the JLS
has slightly different rules from the others relative to type
compatibility. The word "cast" does have a meaning, to me at least,
that is indeed rather narrower than the general concept of type
conversion. I suspect that you will find many others around here who
will assume that definition, but you may also find others who, like
yourself, use it in a less precise sense.

Thank you, I will keep that in mind.

Ray
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

generics puzzle 57
subclassing and generics 19
generics and type checking 9
1.5: generics warning of using Raw datatype 7
Tree design with generics 2
Class Generics 4
JDK5 Generics 6
Generics compiler warning 3

Members online

Forum statistics

Threads
473,768
Messages
2,569,574
Members
45,051
Latest member
CarleyMcCr

Latest Threads

Top