Originally posted by Ernest Friedman-Hill: Incidentally, it's widely considered that having String be a subclass of Object was a design flaw in Java. Watch this:
Line (1) is legal because you can assign a subclass object to a superclass variable without a cast. Line 2, although it compiles, gets an ArrayStoreException at runtime, as you can't put an Object into a String! This violates the Liskov Subtitution Principle, which is widely held to be part of the definition of proper polymorphic behavior. The Principle states that anything you can do to a superclass object, you can do to any subclass object; you can see that it doesn't hold in this case.
I think that the important thing you've demonstrated is that the notion of subclassing, or subtyping, is context sensitive. I'd argue that the Liskov Substitution Principle certainly on the surface presents a good ideal to strive for as we develop well-designed (polymorphic) systems, but it's only a dream. Alistair Cockburn's paper, Constructive Deconstruction of Subtyping, demonstrates a violation of this principle in every subclass relationship in Java. Does that mean the entire language is a design flaw? Maybe. But again, I think that the important thing to keep in mind is not just that the Liskov Substitution Principle can help us to write well-designed systems, but also that in the real world of Java programming, the notion of sybtyping is context sensitive. [ April 11, 2004: Message edited by: Dirk Schreckmann ]
Thanks for bringing this up, as it's an interesting discussion. Mr. Cockburn is correct, really, in that the reflective example shows how reflection destroys the notion of subtype. If you look at it, it's tautological to say that if LSP can't hold if you count meta-queries about type as object operations. You could do the same thing in C++ using sizeof(). Most of this paper is old news, though; it's just that the other (nonreflective) examples are much more conventionally, not to mention convincingly and comprehensibly, explained by the common arguments about how it's wrong to extend concrete types. If Ellipse has separately mutable major and minor axes, then you shouldn't have derived Circle from it in the first place, as Circle doesn't have those operations. If Ellipse needs them, then both Circle and Ellipse may need to be derived from a more general abstract ConicSection class. If you derive Circle from Ellipse, then of course LSP is going to be violated. Another way to say this: A.C.'s paper seems to assume that being a subclass means being a subtype. It's not, really, any more than implementing equals() and hashCode() means that a class has upheld the contracts of those methods. It's possible to get it wildly wrong, but this doesn't imply there's a problem with equals() and hashCode() -- it means that the programmer has made a mistake. Is all of Java flawed? Not fundamentally, but there are a number of absolutely barbarous LSP violations in the APIs. My favorite (maybe everybody's after this array thing) is java.util.Stack, which, being derived from Vector, gives random access to its contents, which a Stack by definition doesn't. I think A.C. is a smart guy, but I think he missed the mark in this paper.
Hi all, The short conclusion for me:array is special. In code 2),the 'object' variable pointing to a string object.So the casting is correct since the 'object' really is a String after the statement bject="one". In code 1),although all elements of the 'objects' variable are pointing to Strings after the statements,the 'objects' variable is still an object array.The casting is correct if and only if the 'objects' variable is defined as a String. Your comments is welcome. xusoo
Originally posted by Ernest Friedman-Hill: If Ellipse has separately mutable major and minor axes, then you shouldn't have derived Circle from it in the first place, as Circle doesn't have those operations.
And that "if" is not obvious, either. What does an Ellipse represent? If it represents a specific ellipse (note lack of capitalization), then Ellipse ought not be mutable, and Circle can safely be derived from it. If it's something that can be altered to represent any ellipse, and a Circle is only something that can be altered to represent any circle, then a Circle is clearly not an Ellipse, and Circle should not inherit from Ellipse. It seems to me that the relationship between Object and String is more like the latter situation, and it was probably a mistake to make it an inheritance relationship. Is there any advantage gained from the inheritance relationship here? I would note that if Java had multiple inheritance, I might feel differently about this situation.
Joined: Dec 10, 2001
Note that Xu Song's inquiry above is relevant to a thread in the Beginners forum, where it has been repeated. Please direct responses to the Beginners forum thread. It seems to me that the relationship between Object and String is more like the latter situation, and it was probably a mistake to make it an inheritance relationship. Is there any advantage gained from the inheritance relationship here? The benefits include utility methods like those found in the Arrays class that work on object arrays, and the toArray(Object) method of the List interface. Without generics, I can't think of another way to achieve these.
author and iconoclast
Without generics, I can't think of another way to achieve these.
You're right, I can't either -- at least, not without an escape to native codem the way that System.arraycopy() does it.
Joined: Dec 10, 2001
Well, actually I think I can think of another way where these utility methods would take a plain Object reference, instead of an Object reference, and use a little reflection trickier. Perhaps a second argument to specify the component type would be nessary. Whatever... On a side note, if I correctly recall the Generics and other Java 1.5 language enhancements BoF with Joshua Bloch at Java ONE 2003, a List<String> will not be a sub-type of a List<Object>. (So, we'll be freed to worry about other things.) [ April 12, 2004: Message edited by: Dirk Schreckmann ]