Prev: Call for Papers: International Conference on Internet and Multimedia Technologies ICIMT 2010
Next: Is there a 'Filter' or 'Predicate' interface anywhere in theJDK?
From: Tom Anderson on 28 Jun 2010 16:43 On Mon, 28 Jun 2010, Lew wrote: > Tom Anderson wrote: > >> And of course, it goes without saying that: >> >> Hand.getBird().equals(Collections.asList(Bush.getBird(), Bush.getBird())) > > That works as a pun, but as Java code would fail to yield 'true' under > conformant implementations of 'equals()'. I forgot to mention that Bird implements List<Bird>. Weird, but there you go. tom -- Fitter, Happier, More Productive.
From: Tom Anderson on 28 Jun 2010 16:52 On Mon, 28 Jun 2010, Lew wrote: > Arne Vajh?j wrote: >>> And in general those figure examples often lead to problems like the >>> wellknown Rectangle versus Square problem. > > ClassCastException wrote: >> A problem that goes away when you make the durn things immutable. > > Not really, but it does go away when you make the classes final. You mean both of them? Then you either have to introduce a common base class ParallelRightQuadrilateral, or be unable to write code that manipulates boxes uniformly. Making Square a subtype of Rectangle is still very attractive from a programmer-friendliness point of view. Immutability is one way of overcoming the problems that introduces - and it really does solve them. Squares are only non-Rectangular when they'r mutable, because it's only their mutability that is not a superset of Rectangle's tom -- Fitter, Happier, More Productive.
From: Lew on 28 Jun 2010 17:30 Tom Anderson wrote: >>> And of course, it goes without saying that: >>> >>> Hand.getBird().equals(Collections.asList(Bush.getBird(), Bush.getBird())) > Lew wrote: >> That works as a pun, but as Java code would fail to yield 'true' under >> conformant implementations of 'equals()'. > Tom Anderson wrote: > I forgot to mention that Bird implements List<Bird>. Weird, but there you > go. > That still would not be conformant, because the requirement for 'List#equals()' is, "Returns true if and only if the specified object is also a list, both lists have the same size, and all corresponding pairs of elements in the two lists are equal." A List with one element cannot be equal to a List with two elements under a conformant 'equals()'. Besides, it's not true that a bird in the hand is *equal to* two in the bush, it's that a bird in the hand is *worth* two in the bush. -- Lew
From: Lew on 28 Jun 2010 17:31 ClassCastException wrote: >>> A problem that goes away when you make the durn things immutable. > Lew wrote: >> Not really, but it does go away when you make the classes final. > Tom Anderson wrote: > You mean both of them? Then you either have to introduce a common base > class ParallelRightQuadrilateral, or be unable to write code that > manipulates boxes uniformly. Making Square a subtype of Rectangle is still > very attractive from a programmer-friendliness point of view. Immutability > is one way of overcoming the problems that introduces - and it really does > solve them. Squares are only non-Rectangular when they'r mutable, because > it's only their mutability that is not a superset of Rectangle's > Oh, yeah. You're right. -- Lew
From: ClassCastException on 29 Jun 2010 00:21
On Mon, 28 Jun 2010 20:31:40 -0700, Peter Duniho wrote: > Arne Vajhøj wrote: >> [...] >>> It's debatable whether one really needs methods in the type that >>> return modified versions of the original. But assuming one does, >> >> It is rather common to have such. The Java API itself has some >> important ones. > > Really? Where is the Square type in the Java API that inherits > Rectangle, never mind the immutable version that returns modified > versions of the original? > >>> could easily enforce Rectangles and Squares, either by throwing >>> exceptions if misused (which is actually possible even with mutable >>> types, though would involve more work), or by providing methods that >>> always return the right kind of type (Square if the new dimensions are >>> equal, Rectangle otherwise). >> >> What should the formal return type be? > > Whatever you want it to be. One option is Rectangle. If it matters > whether the resulting type is a Square (e.g. Rectangle.FitInSquare()), > you can check and cast. > > If you want the return type to be Square, you have to throw an exception > if someone tries to return a non-Square from a Square. Or you provide > an API that only returns a Square (e.g. adjusts both dimensions by > identical amounts). > >> The sub class can return the super class but not the other way around. > > Rectangle can return a Square instance and Square can return a > Rectangle. I have no idea why you think you can't do it that way. > > Of course, whatever API is chosen, it needs to make sense in context. > But there's no fundamental reason Rectangle can't return a Square. > >>> There's no reason for weird behavior to exist. Just don't implement >>> weird behavior. :) >> >> Difficult to avoid if the classes should have rich functionality. > > Now you are adding requirements not originally stipulated. No one said > anything about "rich functionality", nor is it even clear what is meant > in the case of the Square/Rectangle example. > > Pete How about making the example more concrete, then. public class Rectangle { protected final double l; protected final double t; protected final double w; protected final double h; public Rectangle (double left, double top, double width, double height) { if (width <= 0.0) throw new IllegalArgumentException(); if (height <= 0.0) throw new IllegalArgumentException(); l = left; t = top; w = width; h = height; } // getters go here, and getRight and getBottom public Rectangle getScaledInstance (double scaleFactor) { if (scaleFactor == 0.0) throw new IllegalArgumentException(); // top left corner is center of scaling transform's effect if (scaleFactor < 0.0) return new Rectangle(l + w*scaleFactor, t + h*scaleFactor, -w*scaleFactor, -h*scaleFactor); return new Rectangle(l, t, w*scaleFactor, h*scaleFactor); } public Rectangle getWithWidth (double newWidth) { if (newWidth <= 0.0) throw new IllegalArgumentException(); return new Rectangle(l, t, newWidth, h) } public Rectangle getWithHeight (double newHeight) { if (newHeight <= 0.0) throw new IllegalArgumentException(); return new Rectangle(l, t, w, newHeight) } public Rectangle getMoved (double newLeft, double newTop) { return new Rectangle (newLeft, newTop, w, h); } } public class Square extends Rectangle { public Square (double left, double top, double size) { super(left, top, size, size); } public Square getScaledInstance (double scaleFactor) { if (scaleFactor == 0.0) throw new IllegalArgumentException(); // top left corner is center of scaling transform's effect // w = h = size if (scaleFactor < 0.0) return new Square(l + w*scaleFactor, t + h*scaleFactor, -w*scaleFactor); return new Square(l, t, w*scaleFactor); } public Square getMoved (double newLeft, double newTop) { return new Square (newLeft, newTop, w); } } The "copy-mutator" methods that would preserve squareness return Squares. The ones that could produce non-square rectangles aren't even overridden, and return Rectangles. This works, though it's possible to get square Rectangles that are not Squares. Making the constructor protected, adding a public factory method that calls it normally but only after if (width == height) return new Square(left, top, width);, and changing the "copy- mutators" to call this factory method would address this, if one felt it necessary. Of course if there's any problem with the above let me know and I'll try to address it. |