aspose file tools*
The moose likes Beginning Java and the fly likes Should Abstract Classes Favor Nullary Constructors? Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login
JavaRanch » Java Forums » Java » Beginning Java
Bookmark "Should Abstract Classes Favor Nullary Constructors?" Watch "Should Abstract Classes Favor Nullary Constructors?" New topic
Author

Should Abstract Classes Favor Nullary Constructors?

Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 523
    
    3

Suppose I have an abstract class that defines a constructor with an argument:



I believe that any concrete extension of that class will have to implement a non-nullary constructor to, at least, invoke the superclass constructor, like this:



That's not a lot of code, but it must show up in every concrete subclass. To avoid that, I could move the assignment to the "cousin" field out of the constructor:



This lets me leave out the constructor in the subclass, but requires me to replace code like this:

with code like this:

It also means the cousin field of the parent class is no longer final.

To be tricky, one could collapse the above two lines if setCousin() returned its object reference. That is, this:

Allows me to use this:

although that requires the cast down from Parent to Child before the assignment, which is maybe mildly vulgar.

I would be grateful to know if others have a preference or superior option here. Put as a single question, perhaps I am asking, "Should abstract classes have nullary constructors, in order to avoid requiring superclass calls in all concrete subclass constructors?"
Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 7631
    
  19

Stevens Miller wrote:I would be grateful to know if others have a preference or superior option here. Put as a single question, perhaps I am asking, "Should abstract classes have nullary constructors, in order to avoid requiring superclass calls in all concrete subclass constructors?"

Not quite sure what you're asking here. Are you saying that if a subclass doesn't want to provide a Cousin value, that it is given a default?

Simplest to me would seem to be:However, if this is a more general question about defaults, or you may have many of these parameters, I'd have a look at the Builder pattern.

Winston

Isn't it funny how there's always time and money enough to do it WRONG?
Articles by Winston can be found here
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 523
    
    3

No, I'm not asking about defaults. I'm trying to find a way to avoid writing constructors for subclasses when the parent class has a non-nullary constructor. Unless there is some way to do that I don't know about, it seems that, when your parent class only has a non-nullary constructor, your child class must have a constructor that (even if it does nothing else) calls the parent class constructor. That means writing at least this much code in every child class:

Well, to be precise, the child class's constructor's signature wouldn't have to match the parent class's constructor's signature, but the child's constructor would have to call the parent's constructor.

Another way to ask my question is, "Is it good practice to provide nullary constructors for extensible classes, to avoid the need for their subclasses to call super() in their own constructors?"

Maybe this is a non-issue, but it came up when I was looking over some code I wrote this week. My code involves one abstract class from which I have implemented six concrete classes. The abstract class has one constructor and it is non-nullary. Thus, all of my concrete classes have constructors, all of which have one line in their bodies, calling super(fw) (where "fw" is the argument passed to the constructor). It just seemed silly to force all my extended subclasses to have identical constructors like that when one of the virtues of a superclass is that you can centralize code in them and have it be shared by all the subclasses. But, constructors by definition must have the same name as their class, so you can't call, say "Child(cousin)" and have that be shared; the Child() constructor must be defined in the Child class. So, my idea was to move the passage of the argument out of the constructor in the parent class (since the parent class constructor can't be shared by the child classes without an explicit super() call) and into a setter that can be shared by the child classes (thus relieving me of having to add the explicit super() call to all of those child classes).

Admitting, as I try to do on at least a daily basis, that I can be A Real Dumb-Ass sometimes, I thought I'd ask for guidance.
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 523
    
    3

Btw, I would have expected you (of all people) to write your example this way:
Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 38334
    
  23
By “nullary constructor” I presume you mean no‑arguments constructor. I think nullary is not a proper Java term; I couldn't find it in the Language Specification index.

You should design your constructors to match the class they are in. Remember they are not inherited, so you have to provide constructors in all the subclasses. If your superclass has fields, you need to consider whether a particular constructor can initialise those fields to a sensible and consistent value (or, more formally, establish the class' invariants). If a particular constructor cannot reliably do that, it is probably best to delete it. In inheritance, that means any constructor called with a super(...); call can be relied on to establish the invariants.
It would be wrong to provide a constructor to obviate the need for a super(...); call.
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 523
    
    3

Campbell Ritchie wrote:By “nullary constructor” I presume you mean no‑arguments constructor.

Yes, that's what I mean.

I think nullary is not a proper Java term; I couldn't find it in the Language Specification index.

It shows up in the Java API Specification. I encountered it while reading about the Encoder class.

You should design your constructors to match the class they are in. Remember they are not inherited, so you have to provide constructors in all the subclasses. If your superclass has fields, you need to consider whether a particular constructor can initialise those fields to a sensible and consistent value (or, more formally, establish the class' invariants). If a particular constructor cannot reliably do that, it is probably best to delete it. In inheritance, that means any constructor called with a super(...); call can be relied on to establish the invariants.

That makes sense, thanks. Affirmation of this approach appears to be on p.255 of "Head First Java" (2nd Ed), which does provide an example that simply has subclass constructors calling super() with the arguments needed by superclass constructors. I can live with that; was just asking because it seemed anathema to the basic notion that shared methods belong in the superclass. As you point out, however, constructors are not inherited, and that makes all the difference.

It would be wrong to provide a constructor to obviate the need for a super(...); call.

I'm not quite sure I am parsing you correctly. Do you mean it would be wrong to provide a subclass constructor that tried to accomplish what its superclass constructor does, so that the subclass constructor wouldn't have to call super()? (I would agree, btw.) Or, do you mean it would be wrong to include a superclass constructor written in a way that excused subclass constructors from calling super()? (With which I would also agree.)
Winston Gutkowski
Bartender

Joined: Mar 17, 2011
Posts: 7631
    
  19

Stevens Miller wrote:Or, do you mean it would be wrong to include a superclass constructor written in a way that excused subclass constructors from calling super()?

The fact is, you can't excuse subclass constructors from calling super(); and especially, you can't excuse them from calling super(...).
The first can be skipped because it will be supplied by the compiler; but I'd care to bet that the .class file will be exactly the same whether you write it explicitly or not.

I'm still not quite sure why you think you need a way to save yourself writing one extra line per subclass. If there's something involved in generating a Cousin object, then surely that's what you need to rationalize?

But maybe I'm just being thick...

Winston
Jeff Verdegan
Bartender

Joined: Jan 03, 2004
Posts: 6109
    
    6

Winston Gutkowski wrote:
Stevens Miller wrote:Or, do you mean it would be wrong to include a superclass constructor written in a way that excused subclass constructors from calling super()?

The fact is, you can't excuse subclass constructors from calling super(); and especially, you can't excuse them from calling super(...).
The first can be skipped because it will be supplied by the compiler; but I'd care to bet that the .class file will be exactly the same whether you write it explicitly or not.


Yup, identical.
Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 38334
    
  23
Stevens Miller wrote: . . .[nullary] . . . shows up in the Java API Specification . . .about the Encoder class.
I take it back, then.
. . .
I'm not quite sure I am parsing you correctly. Do you mean it would be wrong to provide a subclass constructor that tried to accomplish what its superclass constructor does, so that the subclass constructor wouldn't have to call super()? (I would agree, btw.) Or, do you mean it would be wrong to include a superclass constructor written in a way that excused subclass constructors from calling super()? (With which I would also agree.)
I mean that you don't want a constructor simply so you can rely on the default super(); call inserted by the compiler. I didn't phrase that at all well, did I?
Matthew Brown
Bartender

Joined: Apr 06, 2010
Posts: 4363
    
    8

My take on it is that I wouldn't add a no-arg constructor just to make it easier to subclass, for two reasons:

- If I haven't already added a no-arg constructor it's because the class should always be initialised with a value. If that's the case, I don't want to break that invariant just to make coding subclasses slightly more convenient.

- Similarly, if I haven't already added a setter for the field it's because I don't want it to be mutable. If so, I don't want to change that simply to make things more convenient for subclasses.
Stevens Miller
Ranch Hand

Joined: Jul 26, 2012
Posts: 523
    
    3

Winston Gutkowski wrote:
Stevens Miller wrote:Or, do you mean it would be wrong to include a superclass constructor written in a way that excused subclass constructors from calling super()?

The fact is, you can't excuse subclass constructors from calling super(); and especially, you can't excuse them from calling super(...).
The first can be skipped because it will be supplied by the compiler; but I'd care to bet that the .class file will be exactly the same whether you write it explicitly or not.

I'm still not quite sure why you think you need a way to save yourself writing one extra line per subclass. If there's something involved in generating a Cousin object, then surely that's what you need to rationalize?

But maybe I'm just being thick...

Winston

It's all simply a matter of looking over my code and seeing that identical methods existed in every subclass of my abstract class. Ordinarily, that's a clear call for refactoring (by moving those methods up into the abstract class). But, the wrinkle here is that the methods in question were the subclasses' constructors.

You can't excuse a subclass constructor from calling super(), but you can relieve the programmer from writing the call (because, as you said, the compiler will supply it if it is nullary). That's all I was asking about, really.

And, I think I've got my answer, so we'll mark this thread resolved.

Thanks again, everyone!
Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 38334
    
  23
Stevens Miller wrote: . . . identical methods existed in every subclass of my abstract class. . . . But, the wrinkle here is that the methods in question were the subclasses' constructors.

You can't excuse a subclass constructor from calling super(), but you can relieve the programmer from writing the call (because, as you said, the compiler will supply it if it is nullary). . . .
Thanks again, everyone!
Constructors aren't methods and aren't inherited. Writing a no‑arguments constructor to excuse the programmer from using a super(x, y, z); call in a subclass, and thereby failing to establish the class' invariants sounds like poor design still.

There is a way to refactor that:-
  • 1: Move as many of the fields as possible into the superclass and remove them from the subclasses.
  • 2: Give each class as few constructors as you can, and make sure every constructor establishes the class' invariants.


  • And, “You're welcome”.
    Stevens Miller
    Ranch Hand

    Joined: Jul 26, 2012
    Posts: 523
        
        3

    Campbell Ritchie wrote:Constructors aren't methods...

    Ah, you're right about that. Hadn't ever considered it, but they aren't applied to instances, are they? Nor are they the same as static methods. Valuable point to keep in mind.

    Writing a no‑arguments constructor to excuse the programmer from using a super(x, y, z); call in a subclass, and thereby failing to establish the class' invariants sounds like poor design still.

    Agreed.

    There is a way to refactor that:-
  • 1: Move as many of the fields as possible into the superclass and remove them from the subclasses.
  • 2: Give each class as few constructors as you can, and make sure every constructor establishes the class' invariants.


  • That's kind of where I started, since my subclass constructors all had one line in their bodies, which was "super(...)." That is, there were no fields to promote into the superclass, and each subclass only had one constructor. I know that must seem like I was making much ado about nothing, but I've simply never encountered the situation before, so I had to go through this analysis to see the wisdom of where it has ended.
    Jeff Verdegan
    Bartender

    Joined: Jan 03, 2004
    Posts: 6109
        
        6

    Stevens Miller wrote:
    Campbell Ritchie wrote:Constructors aren't methods...

    Ah, you're right about that. Hadn't ever considered it, but they aren't applied to instances, are they?


    Yes, they are applied to instances. That is a constructor can only run when there's an instance for it to run on.The main difference between constructors and methods is that we cannot invoke an object's constructor at arbitrary time. Any given constructor will run at most once for a given object, at a strictly defined point in that object's lifecycle, and at least one constructor must run for every object created.

    Mainly, though, constructors aren't methods because the JLS defines that they are not.
    Stevens Miller
    Ranch Hand

    Joined: Jul 26, 2012
    Posts: 523
        
        3

    Jeff Verdegan wrote:
    Stevens Miller wrote:
    Campbell Ritchie wrote:Constructors aren't methods...

    Ah, you're right about that. Hadn't ever considered it, but they aren't applied to instances, are they?


    Yes, they are applied to instances. That is a constructor can only run when there's an instance for it to run on.

    Sure, but you can't do something like this:

    The main difference between constructors and methods is that we cannot invoke an object's constructor at arbitrary time. Any given constructor will run at most once for a given object, at a strictly defined point in that object's lifecycle, and at least one constructor must run for every object created.

    I agree with your latter statement, but I'm not sure if I would agree that invocation isn't possible at "arbitrary" times. I can create an instance at an arbitrary time, can't I? Seems you meant, "at an arbitrary time in that object's lifecycle," which I think is correct.

    Mainly, though, constructors aren't methods because the JLS defines that they are not.


    No arguing with that.
    Jeff Verdegan
    Bartender

    Joined: Jan 03, 2004
    Posts: 6109
        
        6

    Stevens Miller wrote:
    The main difference between constructors and methods is that we cannot invoke an object's constructor at arbitrary time. Any given constructor will run at most once for a given object, at a strictly defined point in that object's lifecycle, and at least one constructor must run for every object created.

    I agree with your latter statement, but I'm not sure if I would agree that invocation isn't possible at "arbitrary" times. I can create an instance at an arbitrary time, can't I? Seems you meant, "at an arbitrary time in that object's lifecycle," which I think is correct.


    That's why my next sentence included the phrase "strictly defined point in that object's lifecycle." I kind of figured that would indicate what I meant when I said it couldn't be invoked at an arbitrary time.
    Campbell Ritchie
    Sheriff

    Joined: Oct 13, 2005
    Posts: 38334
        
      23
    Jeff Verdegan wrote: . . . the JLS defines that they are not.
    That means that everybody creating a Java implementation must ensure their constructors are not methods.
    Mike Simmons
    Ranch Hand

    Joined: Mar 05, 2008
    Posts: 3007
        
        9
    Campbell Ritchie wrote:
    Jeff Verdegan wrote: . . . the JLS defines that they are not.
    That means that everybody creating a Java implementation must ensure their constructors are not methods.

    Well, I'd say that javac already guarantees that. The issue is, programmers need to be sure that the things they think are constructors are not, in fact, methods.
    Stevens Miller
    Ranch Hand

    Joined: Jul 26, 2012
    Posts: 523
        
        3

    You gentlemen have morphed this thread into a discussion about something that is the equivalent of a legal issue, not a technical one.

    If the JLS stands in relation to Java in the same way as, say, the United States constitution stands in relation to the US government, then I would say that Jeff is correct as a matter of legal theory: the constitution is never wrong, by definition; the JLS, similarly, defines the language. If ever there were a version of javac that accepted input treating constructors as though they were methods, that version of javac would not be compiling Java; it would be compiling something that wasn't Java.

    Not that this relates much (that I can see, anyway) to my original question, but I rarely get to practice law in this forum, so I couldn't resist this opportunity.
    Mike Simmons
    Ranch Hand

    Joined: Mar 05, 2008
    Posts: 3007
        
        9
    Mmmm, my point (and perhaps Campbell's) was in fact a technical one as well: people can accidentally create a method when they think they're creating a constructor, if they put in a return type. This leads to confusing behavior of the program. So, it's important for programmers to understand the distinctions between constructors and methods.

    On the other hand, if Java had simply referred to constructors as "constructor methods" and described this as a third class of method, similar but separate from static methods and instance methods - that would have been fine, and they could have easily enacted the same rules using different terminology. In that sense, it's just an arbitrary choice of terminology. But people would still need to understand the difference between "constructor methods" and other methods, just as they need to understand differences between static and instance methods.
    Campbell Ritchie
    Sheriff

    Joined: Oct 13, 2005
    Posts: 38334
        
      23
    Stevens Miller wrote: . . . If ever there were a version of javac that accepted input treating constructors as though they were methods, that version of javac would not be compiling Java; . . .
    Yes, that is what I meant. Phrased completely differently, but what I meant.
    Mike Simmons
    Ranch Hand

    Joined: Mar 05, 2008
    Posts: 3007
        
        9
    Hmm, OK. I guess I thought that went without saying, at least in Beginner; there's a lot of stuff that the people who implement a compiler need to get straight, that isn't generally relevant here.
    Stevens Miller
    Ranch Hand

    Joined: Jul 26, 2012
    Posts: 523
        
        3

    Mike Simmons wrote:Mmmm, my point (and perhaps Campbell's) was in fact a technical one as well: people can accidentally create a method when they think they're creating a constructor, if they put in a return type.

    Wow! I didn't know that (and would have lost a bet on it if you hadn't just told me).

    The differences between the types of analyses one applies in computer science and in legal theory are remarkably few. Each grapples with the application of defined systems to practical problems, sometimes made harder to solve because the systems' definitions have unintended implications. I got a fair number of professors to admit that my computer background appeared to be an advantage for me. Certainly, I find good programmers grasp legal problems more effectively than most people who are not good programmers. A classic example is the bane of every law student's life, The Rule Against Perpetuities. Ask a good coder to reduce it to some formalized logical expression (a Java method, for example), and they can pretty much all understand it. Give it to a person who graduated near the top of their class in Political Science (and who can't write code), and they usually find it baffling.

    It all just comes together, sometimes.
    Jeff Verdegan
    Bartender

    Joined: Jan 03, 2004
    Posts: 6109
        
        6

    Stevens Miller wrote:
    Mike Simmons wrote:Mmmm, my point (and perhaps Campbell's) was in fact a technical one as well: people can accidentally create a method when they think they're creating a constructor, if they put in a return type.

    Wow! I didn't know that (and would have lost a bet on it if you hadn't just told me).


    It's a pretty common beginner mistake, actually.

    I've seen quite a number of posts that go something like



    Why is it telling me "cannot find symbol symbol : constructor Foo(int)"?


    public void Foo(...) is legal in the language, but it doesn't mean what people sometimes think it does.
    Stevens Miller
    Ranch Hand

    Joined: Jul 26, 2012
    Posts: 523
        
        3

    Jeff Verdegan wrote:I've seen quite a number of posts that go something like
    Why is it telling me "cannot find symbol symbol : constructor Foo(int)"?

    public void Foo(...) is legal in the language, but it doesn't mean what people sometimes think it does.

    Yes, I've seen those too, but always (wrongly) assumed it wasn't even legal as Java.

    Can you think of any situation where that would be good practice?
    Mike Simmons
    Ranch Hand

    Joined: Mar 05, 2008
    Posts: 3007
        
        9
    I can't. I think it's just something they didn't think to outlaw in the first place, and now they're reluctant to add a rule against it, because it might break someone's backwards compatibility. But I don't think there would ever be a good reason to name a method with the class name.
    Campbell Ritchie
    Sheriff

    Joined: Oct 13, 2005
    Posts: 38334
        
      23
    Stevens Miller wrote: . . . Can you think of any situation where that would be good practice?
    No.
    Agree 100% with Mike Simmons on that point.

    If you use IDEs, you can set options to mark methods masquerading as constructors as errors or warnings.
     
    I agree. Here's the link: http://aspose.com/file-tools
     
    subject: Should Abstract Classes Favor Nullary Constructors?