Article Discussion
Java Design Issues
Summary: Ken Arnold, the original lead architect of JavaSpaces, talks with Bill Venners about whether to prohibit subclassing, whether to use Cloneable or copy constructors, and when to use marker interfaces.
22 posts.
The ability to add new comments in this discussion is temporarily disabled.
Most recent reply: December 15, 2003 2:14 PM by Rover
    Bill
     
    Posts: 409 / Nickname: bv / Registered: January 17, 2002 4:28 PM
    Java Design Issues
    October 13, 2002 11:40 PM      
    "I am not fond of copy constructors. In fact, I'm not very fond of constructors at all. The problem is that the code that creates the object with a constructor is defining the object's type. In all other operations, the code that uses an object effectively only defines that the object is at least a certain type. Constructors are an exception to that rule. I don't think that exception should exist," says Ken Arnold in this Artima.com interview:

    http://www.artima.com/intv/issues.html
    • johnnyo
       
      Posts: 5 / Nickname: johnnyo / Registered: October 16, 2002 11:29 AM
      Re: Java Design Issues
      October 17, 2002 0:10 PM      
      I enjoyed this point:

      "If you look at a method contract, you'll see that not everything can be spelled out in the programming
      language. Some things expressed in the contract must be expressed in human language. String's
      equalIgnoreCase is a method, but nothing in the programming language enforces that case is ignored;
      that requirement is only expressed in the human language text."

      It emphasizes that a certain amount of respect (for want of a better word) needs to be accorded to the English description of a method - its javadoc. One often hears references to the metric of "uncommented lines of code", for instance. The message of such a phrase is that "what really matters is the code, and the comments are really secondary." But I would disagree with that sentiment. Javadoc forms the contract of a method. What could be more important than that? One might even take the contrary point of view : "what really matters is the spec(the javaodc) of the method; all the rest is just a detail of implementation."

      What do you think?
      • Taylor
         
        Posts: 1 / Nickname: taylor / Registered: October 17, 2002 1:19 PM
        Re: Java Design Issues
        October 17, 2002 5:22 PM      
        > than that? One might even take the contrary point of
        > view : "what really matters is the spec(the javaodc)
        > of the method; all the rest is just a detail of
        > implementation."
        >
        > What do you think?

        I think whoever takes that point of view will be attacked by an agry mob of eXtreme programmers.
        • Matt
           
          Posts: 62 / Nickname: matt / Registered: February 6, 2002 7:27 AM
          Re: Java Design Issues
          October 18, 2002 9:24 AM      
          > There is a paper out there somewhere ("Inheritance
          > Decomposed" by Peter Frohlich) that describes pretty
          > nicely how subclassing can be replaced with better
          > techniques in every case.

          Hmm... Sounds like this fellow needs to get a leash for his dogma.

          Of course, you could also replace all fors and whiles with labels and gotos and write a paper about that, too. Well, fortunately, you couldn't do that will Java.

          Essentially, it is ridiculous to say there is one right way to do everything. Especially when that one right way is more difficult.

          It can be argued that composition in many cases is less flexible than extension, simply because composition requires more work; this cements it in, making it less flexible.
          • Jared
             
            Posts: 6 / Nickname: jared / Registered: October 16, 2002 3:13 AM
            Re: Java Design Issues
            October 18, 2002 9:49 AM      
            > Essentially, it is ridiculous to say there is one
            > right way to do everything. Especially when that
            > one right way is more difficult.

            Maybe. But I find that whenever I push myself to think of a composition-based alternative to a design that I've come up with that's extension-based, I end up with a better design. The components I end up creating, rather than simply being the abstract basis for subclasses, become reusable in other contexts.

            > It can be argued that composition in many cases is
            > less flexible than extension, simply because
            > composition requires more work; this cements it in,
            > making it less flexible.

            When you say "this cements it in", I'm not sure what you mean. The cementing that I dislike is that of a subclass to it's superclass parent; surely that relationship is as cemented as can be.

            Composition is by nature a looser coupling. One object is dependent only on the interface of another. I can plug-in a different object that will suffice as long as it satisfies the same interface; such is not the case with a parent-child relationship. Composition also lends itself to a much richer set of patterns that subclassing.

            Jared
            • Matt
               
              Posts: 62 / Nickname: matt / Registered: February 6, 2002 7:27 AM
              Re: Java Design Issues
              October 24, 2002 11:08 AM      
              > When you say "this cements it in", I'm not sure what
              > you mean. The cementing that I dislike is that of a
              > subclass to it's superclass parent; surely that
              > relationship is as cemented as can be.

              What I mean is this. Let's say I want to create a fancy vibrating version of JButton. JButton has about a million or so methods. If I subclass JButton for this purpose, I only add the little bit of code to make it vibrate when it wants to be pressed. When the Java 1.6 API is released and another 500,000 methods are added to JButton, my code doesn't need to change at all (except for the rare corner case where they defined a new method that clashes with one of mine).

              If I used composition on this problem, I would create a button that contains a JButton; then I would have to write a million pass-through methods. Then, for each release of the Java API, I'd have to create a modified version of my button class to support the different interfaces of JButton.

              > Composition is by nature a looser coupling. One
              > object is dependent only on the interface of another.
              > I can plug-in a different object that will suffice as
              > long as it satisfies the same interface; such is not
              > the case with a parent-child relationship.
              > Composition also lends itself to a much richer set of
              > patterns that subclassing.

              I don't think composition necessarily achieves a looser coupling. Seems to me, your class has to be tightly coupled to any objects that comprise it. If one of those contained classes becomes deprecated, or is not available, then you have to do a lot of work to disentagle your code from it. It also seems like you have to waste a lot of time writing code that is not related to solving the problem at hand, but merely to make the contained objects properly accessible (or just throw in the towel and make them public members).

              In the case where the interface to the class is small, clean and simple, composition is great, but as Ken Arnold mentioned in the interview here, that is unfortunately often not the case in the real world.
    • Bill
       
      Posts: 409 / Nickname: bv / Registered: January 17, 2002 4:28 PM
      Re: Java Design Issues
      October 14, 2002 2:50 PM      
      I brought up many of the design issues I asked Ken Arnold about in my interview with Josh Bloch a few months back. The relevant URLs in which Josh gives his opinions are:

      Documenting for Inheritance or Disallowing It
      http://www.artima.com/intv/bloch12.html

      Copy Constructor versus Cloning
      http://www.artima.com/intv/bloch13.html
      • Jared
         
        Posts: 6 / Nickname: jared / Registered: October 16, 2002 3:13 AM
        Re: Java Design Issues
        October 16, 2002 7:25 AM      
        > Copy Constructor versus Cloning
        > http://www.artima.com/intv/bloch13.html

        Bloch is right about clone being broken, but he doesn't address the problem that copy constructors are not polymorphic in his answer. This is a definite limitation when, say, the client knows only of an interface, rather than of a concrete class.

        I have never created a copy constructor; rather, I prefer a "Copyable" interface with public copy() method as Ken Arnold describes.

        I think Bloch is correct regarding subclassing, though. Subclassing, especially from one concrete class to another, frequently leads to more brittle and less understandable code, in my opinion. (Subclassing of abstract classes is certainly better, because the base class is explicitly designed to be polymorphic.)

        There is a paper out there somewhere ("Inheritance Decomposed" by Peter Frohlich) that describes pretty nicely how subclassing can be replaced with better techniques in every case.
        • Bill
           
          Posts: 409 / Nickname: bv / Registered: January 17, 2002 4:28 PM
          Re: Java Design Issues
          October 16, 2002 1:45 PM      
          > > Copy Constructor versus Cloning
          > > http://www.artima.com/intv/bloch13.html
          >
          > Bloch is right about clone being broken, but he
          > doesn't address the problem that copy constructors
          > are not polymorphic in his answer. This is a
          > definite limitation when, say, the client knows only
          > of an interface, rather than of a concrete class.
          >
          > I have never created a copy constructor; rather, I
          > prefer a "Copyable" interface with public copy()
          > method as Ken Arnold describes.
          >
          I asked Josh Bloch about Ken's point, and Josh basically clarified to me that he considers copy constructors an alternative to cloning, not an exact equivalent replacement. He wasn't thinking of copy constructors that use reflection to find and invoke a copy constructor of the same exact type as Ken described, but just a simple copy constructor that converts the passed object to the class's type, doing type truncation if the passed object is actually a subclass. Josh seemed to feel that a conversion constructor would suffice for what most people wanted most of the time.

          I'm not sure what people would want most of the time. What I think I want is a method like clone that polymorphically gives me a duplicate object of the same class, and an interface (such as Copyable) that has the clone method in it. But on the other hand, in 5 years of C++ I always made copy constructors per Scott Meyers' advice in Effective C++, and I never once encountered a situation in which type truncation was a problem.
        • Ken
           
          Posts: 9 / Nickname: kcrca / Registered: March 8, 2002 4:25 AM
          Re: Java Design Issues
          October 17, 2002 4:29 PM      
          > I think Bloch is correct regarding subclassing,
          > though. Subclassing, especially from one concrete
          > class to another, frequently leads to more brittle
          > and less understandable code, in my opinion.
          > (Subclassing of abstract classes is certainly better,
          > because the base class is explicitly designed to be
          > polymorphic.)

          Although I agree that subclassing is widely overused (just think -- a JButton is a Container!), I have yet to see a language that does delegation well enough to make it easy to solve the problem. Haskell has some nice features here. For example, it allows you to assert that some other type -- one you didn't write -- is (say) a Point because it fulfills the contract, possibly by some adaptor code you've written. If you do this, the thing *is* a Point, and can be used anywhere a Point is needed. This allows you to be anything you need to be without binding it into the definition of the type that everyone must share. If you need that thing to be a Point, but others don't, then it will only be a Point in your code.

          It's an interesting feature. But without something like this, and probably several other features besides, working without inheritence is possible but really ugly, and so will not be done. "No rules without tools." Demanding that people reliably do painful things is not a recipe for success.

          > There is a paper out there somewhere ("Inheritance
          > Decomposed" by Peter Frohlich) that describes pretty
          > nicely how subclassing can be replaced with better
          > techniques in every case.

          Well, I wouldn't go that far, but I suppose it depends how you define "inheritence". In any case, doing this without language support would be awful. (I mean, you *can* do OO programming in C; just look at the X toolkit. But without the language support it's pretty intolerable.)
          • Jared
             
            Posts: 6 / Nickname: jared / Registered: October 16, 2002 3:13 AM
            Re: Java Design Issues
            October 18, 2002 10:02 AM      
            > Although I agree that subclassing is widely overused
            > (just think -- a JButton is a Container!), I have yet
            > to see a language that does delegation well enough to
            > make it easy to solve the problem. Haskell has some
            > nice features here. For example, it allows you to
            > assert that some other type -- one you didn't write
            > -- is (say) a Point because it fulfills the contract,
            > possibly by some adaptor code you've written. If you
            > do this, the thing *is* a Point, and can be used
            > anywhere a Point is needed. This allows you to be
            > anything you need to be without binding it into the
            > definition of the type that everyone must share. If
            > you need that thing to be a Point, but others don't,
            > then it will only be a Point in your code.

            Interesting -- if by "fulfills the contract," you mean that it not only satisfies all the method signatures of a Point but also the *behavior* of a Point, then that sounds good. But of course I would be wary of a tool that told me that Class A satisfied the contract of Interface B simply because it had all the correct method signatures -- that might be mere coincidence. I am not sure how a tool could enforce anything else, though.


            Jared
            • Ken
               
              Posts: 9 / Nickname: kcrca / Registered: March 8, 2002 4:25 AM
              Re: Java Design Issues
              October 18, 2002 11:12 AM      
              > Interesting -- if by "fulfills the contract," you
              > mean that it not only satisfies all the method
              > signatures of a Point but also the *behavior* of a
              > Point, then that sounds good.

              Well, what *else* could it mean? This is why I said that the contract is in English, not just the method signatures.

              > But of course I would
              > be wary of a tool that told me that Class A satisfied
              > the contract of Interface B simply because it had all
              > the correct method signatures -- that might be mere
              > coincidence. I am not sure how a tool could enforce
              > anything else, though.

              The Haskell idea is that some human would make the assertion by providing any necessary adaptor code. For example, if package A's "Point" class took parameters in (x,y) order, and package B's "Point" class took parameters in (y,x) order, someone could make A's points usable in the B package by providing methods that swapped the order. Then anyone, anywhere in the code could simply use an A point wherever a B point was required.

              Obviously this could be done for more sophisticated mappings, but you get the idea.

              Ken
              • Jared
                 
                Posts: 6 / Nickname: jared / Registered: October 16, 2002 3:13 AM
                Re: Java Design Issues
                October 18, 2002 11:39 AM      
                > Well, what *else* could it mean? This is why I said
                > that the contract is in English, not just the method
                > signatures.

                Well, you didn't mention English. But yes, it couldn't really mean anything else and still be useful.

                > The Haskell idea is that some human would make the
                > assertion by providing any necessary adaptor code.
                > For example, if package A's "Point" class took
                > parameters in (x,y) order, and package B's "Point"
                > class took parameters in (y,x) order, someone could
                > make A's points usable in the B package by providing
                > methods that swapped the order. Then anyone,
                > anywhere in the code could simply use an A point
                > wherever a B point was required.

                OK -- this sounds like something Java could pretty easily do if every class had a corresponding interface, because then the class that wanted to be a "Point" would simply implement the Point interface and delegate (or act as the "adaptor" as you describe) to the other object, translating, e.g., parameters (y, x) into (x, y). Is that a correct statement?

                Jared
                • Ken
                   
                  Posts: 9 / Nickname: kcrca / Registered: March 8, 2002 4:25 AM
                  Re: Java Design Issues
                  October 18, 2002 0:07 PM      
                  > > Well, what *else* could it mean? This is why I
                  > said
                  > > that the contract is in English, not just the
                  > method
                  > > signatures.
                  >
                  > Well, you didn't mention English. But yes, it
                  > couldn't really mean anything else and still be
                  > useful.

                  "Contracts are not expressible in anything less than a human language." I agree -- I didn't say "English". I presume one could define them in any sufficiently technically expressive human language.

                  > OK -- this sounds like something Java could pretty
                  > easily do if every class had a corresponding
                  > interface, because then the class that wanted to be a
                  > "Point" would simply implement the Point interface
                  > and delegate (or act as the "adaptor" as you
                  > describe) to the other object, translating, e.g.,
                  > parameters (y, x) into (x, y). Is that a correct
                  > statement?

                  I think Java could pretty easily do what you suggest. I've even heard it suggested, sometimes by me. (I first heard the idea from Guy Steele, who suggested it be legal to say "class foo implements Point", even if Point is a class.

                  But that's not what I'm saying. The problem with this is that the class designer must do this. The Haskell mechanism comes much later. Any programmer can, at any time, assert the mapability. This means that as a user of both packages A and B you can work around some problems by asserting the mapability of A.Point to B.Point.

                  Now to be fair I haven't lived with this feature because I've never programmed in Haskell (I've only read about it). But it seems fundamentally sound, and probably very useful.

                  Ken
                  • Jared
                     
                    Posts: 6 / Nickname: jared / Registered: October 16, 2002 3:13 AM
                    Re: Java Design Issues
                    October 28, 2002 5:30 AM      
                    > "Contracts are not expressible in anything less than
                    > a human language." I agree -- I didn't say
                    > "English". I presume one could define them in any
                    > sufficiently technically expressive human language.

                    Gotcha - I was looking only at this current thread, not at an interview.

                    > I think Java could pretty easily do what you suggest.
                    > I've even heard it suggested, sometimes by me. (I
                    > first heard the idea from Guy Steele, who suggested
                    > it be legal to say "class foo implements Point",
                    > even if Point is a class.
                    >
                    > But that's not what I'm saying. The problem with
                    > this is that the class designer must do this. The
                    > Haskell mechanism comes much later. Any programmer
                    > can, at any time, assert the mapability. This means
                    > that as a user of both packages A and B you can work
                    > around some problems by asserting the mapability of
                    > A.Point to B.Point.

                    I don't see the point of this complicated sounding "mapper" construct when being able to say "Class X implements Y" where Y is a class will do the job well.

                    Say I'm a third party to A.Point and B.Point. I want to create a class than can take A.Point and make it masquerade as a B.Point so I can use some API that deals with B.Points.

                    All I need to do is create a class, MyPoint, that "implements B.Point" as we discussed, and takes an A.Point instance, and delegates/translates the methods of the B.Point interface to the A.Point instance.

                    Thus the suggestion that you and Guy Steele have thought of seems like the most intuitive "mapping" construct to me. It seems to fit nicely within the existing scheme of the language. (Granted, I haven't thought it through at all.)

                    If what Haskell provides is a mapping mechanism merely so that I don't have to create classes to get from A.Point to B.Point, then I'd simply wonder what the value is in not creating classes.

                    Jared
                    • Ken
                       
                      Posts: 9 / Nickname: kcrca / Registered: March 8, 2002 4:25 AM
                      Re: Java Design Issues
                      October 28, 2002 7:54 AM      
                      ]> I don't see the point of this complicated sounding
                      > "mapper" construct when being able to say "Class X
                      > implements Y" where Y is a class will do the job
                      > well.

                      You're talking compile time for the builder of class X. I'm talking compile time for the *user* of classes X and Y. If you write a perfectly good X class, and Jill writes a perfectly good Y class, and *I* -- a user of both -- would like to be able to treat X as a type of Y, then "implements" won't cut it.

                      > All I need to do is create a class, MyPoint, that
                      > "implements B.Point" as we discussed, and takes an
                      > A.Point instance, and delegates/translates the
                      > methods of the B.Point interface to the A.Point
                      > instance.

                      Given:

                      A.Point getCoords();
                      void printCoords(B.point);

                      Now, with a MyPoint model:

                      printCoords(new MyPoint(getCoords()));

                      But with the Haskell model:

                      printCoords(getCoords());

                      With the MyPoint technique, interaction between the two kinds of points must be mediated by the creation of an adaptor object. A model that told the runtime that the two were interchangable would allow all code using A.Point to use B.Point's with the same ease as if the originator of B.Point had subclassed A.Point.

                      Surely that's a non-trivial distinction. If the subtyping, or IsA, concept has power -- and the whole OO notion fundamentally assumes that it does -- then clearly it is more powerful to establish correct IsA relationships. Adaptors aren't IsA -- they're CanBeMadeToActAsA. And they have the resulting conceptual (and programming) weight. Would you rather create a new MyPoint object for every B.Point object, or provide one set of methods that allow every B.Point to be directly used as an A.Point?

                      Ken
        • Bill
           
          Posts: 409 / Nickname: bv / Registered: January 17, 2002 4:28 PM
          Re: Java Design Issues
          October 16, 2002 1:54 PM      
          > I think Bloch is correct regarding subclassing,
          > though. Subclassing, especially from one concrete
          > class to another, frequently leads to more brittle
          > and less understandable code, in my opinion.
          > (Subclassing of abstract classes is certainly better,
          > because the base class is explicitly designed to be
          > polymorphic.)
          >
          > There is a paper out there somewhere ("Inheritance
          > Decomposed" by Peter Frohlich) that describes pretty
          > nicely how subclassing can be replaced with better
          > techniques in every case.

          I've really gotten this same message again and again from lots of different sources. I think it would be interesting to see a language that didn't actually have implementation inheritance, just interface inheritance. It would also be interesting to see a language, perhaps the same one, that does away with publicly accessible constructors in favor of static factory methods. Both Ken Arnold and Josh Bloch kind of praise the merits of factory methods in their interviews. Even James Gosling himself has sometimes talked about a language without implementation inheritance. I asked him about that here:

          http://www.artima.com/intv/gosling34.html
          • Jared
             
            Posts: 6 / Nickname: jared / Registered: October 16, 2002 3:13 AM
            Re: Java Design Issues
            October 18, 2002 10:10 AM      
            > I've really gotten this same message again and again
            > from lots of different sources. I think it would be
            > interesting to see a language that didn't actually
            > have implementation inheritance, just interface
            > inheritance. It would also be interesting to see a
            > language, perhaps the same one, that does away with
            > publicly accessible constructors in favor of static
            > factory methods. Both Ken Arnold and Josh Bloch kind
            > of praise the merits of factory methods in their
            > interviews. Even James Gosling himself has sometimes
            > talked about a language without implementation
            > inheritance. I asked him about that here:
            >
            > http://www.artima.com/intv/gosling34.html

            Ah! I am excited to see him say exactly what I've been trying to say: "[A language] that just does delegation... without an [implementation] inheritance hierarchy."

            Granted, he says that we don't know yet all the problems that might crop up with a delegation-based language. But I think it'd be worth the investigation. I hope he pursues it.

            Jared
          • Tim
             
            Posts: 10 / Nickname: tpv / Registered: December 9, 2002 1:41 PM
            Re: Java Design Issues
            December 9, 2002 6:45 PM      
            > I think it would be interesting
            > to see a language that didn't actually have implementation
            > inheritance, just interface inheritance. It would also be
            > interesting to see a language, perhaps the same one, that
            > does away with publicly accessible constructors in favor
            > of static factory methods.

            Sather comes close.
            Although like most interesting languages, it's pretty much dead.
            http://www.gnu.org/software/sather/
            http://www.gnu.org/software/sather/ICSI_Sather/index.html

            A number of the guys from ICSI ended up at javasoft...
    • johnnyo
       
      Posts: 5 / Nickname: johnnyo / Registered: October 16, 2002 11:29 AM
      Re: Java Design Issues
      October 16, 2002 11:51 PM      
      Regarding declaring classes as final:

      It seems to me that users of Java fall into two very distinct camps - the "frameworker builders" and the "application builders"; the former build APIs to be used by the latter. I would guesstimate that application builders form the majority - say ~80% - of the community of developers.

      A framework builder needs to be very concerned about designing for subclassing, of course, while an application builder *much* less so. I would suggest that an application builder should keep classes final unless there is a compelling reason to do so, since it simplifies the model, and avoids the pitfalls admirably illustrated by Bloch. Changing this decision later on is just another refactoring, which does not affect the consumer of the application in any way...
      • Rover
         
        Posts: 3 / Nickname: rhubarb / Registered: December 15, 2003 8:46 AM
        Re: Java Design Issues
        December 15, 2003 2:03 PM      
        > A framework builder needs to be very concerned about
        > designing for subclassing, of course, while an application
        > builder *much* less so. I would suggest that an
        > application builder should keep classes final unless
        > there is a compelling reason to do so, since it
        > simplifies the model, and avoids the pitfalls admirably
        > illustrated by Bloch. Changing this decision later on is
        > just another refactoring, which does not affect the
        > consumer of the application in any way...

        As an application programmer I tend to agree with this: we have to think much less about it, and we can refactor later.
        But I dont agree with the willy-nilly use of final.
        Firstly: a good application programmer tries to program every part of an application as a reusable framework.

        Secondly: By using final you are saying to all would-be subclassers: "You cannot subclass this because it would be truly evil and I just can't allow it."
        Here you are assuming that there are would-be subclassers. If noone was ever going to subclass your class there's no point in making it or its methods final. And if someone does come to subclass your class, then finding final in the signature is going to scare them. "Why is it final?" "Is something bad going to happen if I refactor it and subclass it?".
        • Rover
           
          Posts: 3 / Nickname: rhubarb / Registered: December 15, 2003 8:46 AM
          Re: Java Design Issues
          December 15, 2003 2:11 PM      
          Regarding the "addAll calls add and should therefore declare it in its documentation" problem.

          I haven't read the full description of this problem from the original source - just the quote in this interview - so I could well be missing something but...

          I don't see the need for documentation here. I have two takes on this in fact.

          The first is: Any class that has a method addAll and another called add, almost certainly has a getCount() or size() or length() method that would obviate the need for doing so in add and addAll. And to those of you who would argue: "Use your imagination bozo this is just an analogy for a real case" I would reply: Don't bother me with the problem unless you can come up with a real world, at least sligthly probable example. Often these abstractions and analogies make the problem easier to understand, but they also tend to make us worry about problems that don't exist.

          The second take, is assuming that there is a real world use for the add/addAll issue, perhaps the best policy is just never to do that. Just do

          public add() {
          • Rover
             
            Posts: 3 / Nickname: rhubarb / Registered: December 15, 2003 8:46 AM
            Re: Java Design Issues
            December 15, 2003 2:14 PM      
            (whoops hit the wrong key, sorry - where was I. Oh yeah.. Just do)

            public void add(Object object) {
            addImpl(object);
            }

            public void addAll(Object[] objects) {
            ...
            addImpl(objects
            ...
            }

            private void addImpl(Object object) {
            ...
            }

            That way you dont have to care about such things in your documentation.

            This would seem to work for all such cases, but are there indeed cases where its impractical not to call your own public methods.