final methods and classes

  • Thread starter Lionel van den Berg
  • Start date
R

Rzeźnik

If you declare a class 'final', then it is obviously known that it
will not be subclassed.

How is it KNOWN? It is not known - you cannot know this except for a
few cases, and even in these cases it is not strictly 'knowledge' but
rather educated guess.

 ("Redefined" does not apply here - we're
talking about inheritance, not redefinition.)

Technically you are correct, although redefinition (of a method or an
invariant) by a subclass is the most interesting case here. I think
that even you do not deem extension without redefinition to be THAT
bad.

 You're putting the cart
before the horse.

No prediction needed - it is a dictatorship.

If you do not declare a class 'final', then you had better darn well
make sure that it's conditioned for inheritance.  If you do not
declare a class 'final', you are giving permission for it to be
inherited.  No prediction needed.

Only if you know all possible use cases in which your class is going
to be used. Not feasible.
The API writer does not predict, he permits.

So he better permits wisely. It is surely safer to forbid everything,
but that makes API less usable (up to and beyond the point of being
useless).
 
A

Andreas Leitgeb

Lew said:
If you declare a class 'final', then it is obviously known that it
will not be subclassed. [...]
No prediction needed - it is a dictatorship.

The prediction is what a developer may do for user's wishes.

The Lew-designer would design streets like trails - only at switches
is it even possible to change direction. Every possible future need for
direction-changes has to be *predicted* at trail-build time, or the
trail cars just will not (dictatorship-enforced) be able to turn
right, later, where the trail passes by his own house.

The Rzeźnik-designer designs streets as they are, at the risk of each
car to slip off the road, maybe even fall down the bridge, at the
car-driver's freedom and full responsibility.

And in Lew's view, Rzeźnik probably would design trails like streets,
and let the trains go any direction never meant for them to go...

Could you two just agree to disagree?
 
M

Mike Schilling

Andreas said:
Probably just like Rzeznik, I do have some problems grasping
this statement.

It seems to me that it means two entirely separate things depending
on one's viewpoint:

Lew: rather than predict what users are going to do with my API,
one had better control them, by using "final" to prevent any
possibly "unintended" uses ...

Rzeznik: Rather than predict, what the user really wants to do with
the API, rather give them a good base and let them override where
they want custom behaviour. Control them by offering them a good
API of public and protected methods so they can't do too nasty
things.

Mike: Unless I've been careful to design class Foo for inheritance, I'm
likely to break its (unintended by me) subclasses if I reimplement it in
version 2. Rather than risk this, I'll mark it final. If someone really
needs the functionaly of subclassing it, they can achieve it safely using
aggregation, which V2.0 is very unlikely to break. And as a matter of
brute fact, I'm very unlikely to derive one concrete class from another.
 
M

Mike Schilling

Tom said:
Nonetheless, it does contain at least one piece of pretty questionable
advice, namely this one.

I say that despite the fact that i haven't read the book, and don't
intend to. I have, however, come across problems in my work which we
could solve by subclassing an existing class in a third-party library
and overriding some of its methods, in a way that its designers
almost certainly did not intend. If they'd taken JoBo's advice of
finalising everything that wasn't explicitly intended to be
overridable, we wouldn't have been able to do that.

Another bit of useful advice from Effective Java: prefer aggregation. No
one said you can't re-use code, but inheritance, particularly unintended
inheritance, is not necessarily the best way to do it.
 
R

Rzeźnik

Another bit of useful advice from Effective Java: prefer aggregation.  No
one said you can't re-use code, but inheritance, particularly unintended
inheritance, is not necessarily the best way to do it.

You cannot really _prefer_ one to another in general because they
represent different relationships. If this Effective Java states it
exactly so then it is another example of how misleading this book is.
Correct way of saying what you just might be: prefer aggregation to
inheritance IFF both of them are valid relations between types in your
particular case.
 
M

Mike Schilling

Rzeznik said:
You cannot really _prefer_ one to another in general because they
represent different relationships. If this Effective Java states it
exactly so then it is another example of how misleading this book is.

I caution you not to judge the book by a two-word paraphrase. At any rate,
if all you want is to re-use code, that's not an is-a relationship.
 
L

Lew

Rzeźnik said:
If you so strongly believe that what you are advocating for is
'recommended practice' then it is not strange that anything I've said

It is not a belief but a verifiable fact. I am advocating a practice,
and it's been recommended by leading authors in the Java world, ergo
it's a recommended practice.
makes no sense to you. Point is that "prohibiting inheritance" is not

Not strange, perhaps, but also not true. What you are saying makes
perfect sense to me, it's only that I don't completely agree with
you. Your mistake is to confuse disagreement with lack of
understanding.
a recommended practice. If you don't design a class to be thread safe
but allow it to be you are in far bigger troubles - do you recommend
putting 'synchronized' everywhere?

No, I don't. I recommend that you either design a class for multi-
threaded use or don't use it in a multi-threaded way. That is cognate
to the discussion here.

I see you haven't stopped doing that. Stop misstating my point then
arguing against the misstatement.

I am not advocating prohibiting inheritance per se. Stop presenting
that as if I were. It's intellectually dishonest. Let me put that in
simple terms again in case you don't understand it:

I do not advocate prohibiting inheritance. I advocate thinking
carefully about whether a class should be heritable, and either
designing the class to be so or else prohibiting it. There are two
options in what I'm proposing, both based on thinking carefully about
what you will permit. One is to permit inheritance, and if so, design
the class accordingly. The alternative is to prohibit inheritance.
Either/or. Not just one. I am not against inheritance. I am against
the abuse of inheritance.

I do not advocate prohibiting inheritance in any blanket way.

Do you understand that now? Or are you going to misstate my point
again?

Maybe you should break down and actually read the referenced chapters
in /Effective Java/ to get the complete discussion - again, more
cohesively and comprehensively presented than I have done - instead of
attacking points I'm not making and attributing those points to me.

--
Lew
This sig (signature passage) copyright (c) 2009 by Lew Bloch. All
rights reserved. Reproduction of this sig in any format, electronic
or otherwise, without express written permission from the copyright
holder is forbidden.
 
R

Rzeźnik

I caution you not to judge the book by a two-word paraphrase.

Ok, you are right.

 At any rate,
if all you want is to re-use code, that's not an is-a relationship.

What makes you think so? Consider SavedGame <- File. while it is clear
that every SavedGame is_a file you certainly do not want to expose
that fact to clients (if you did they could, let's say, save it
anywhere they like or overwrite contents blindly) while still retain
ability of working with files - sheer code re-use. Would you say that
SavedGame has_a File instead?
 
A

Andreas Leitgeb

Lew said:
One is to permit inheritance, and if so, design
the class accordingly.

What makes a class "designed for inheritence"?

Of course documentation - that would go without saying.
What else? Anything else?

Is it the existence of at least one private field with
accessor methods, just to show derivers who's the boss?
 
R

Rzeźnik

It is not a belief but a verifiable fact.  I am advocating a practice,
and it's been recommended by leading authors in the Java world, ergo
it's a recommended practice.


Not strange, perhaps, but also not true.  What you are saying makes
perfect sense to me, it's only that I don't completely agree with
you.  Your mistake is to confuse disagreement with lack of
understanding.

Ok, I understand.
No, I don't.  I recommend that you either design a class for multi-
threaded use or don't use it in a multi-threaded way.  That is cognate
to the discussion here.

You are a little illogical here, you know. Why do not 'forbid' such
erratic behavior?
Here your "don't use" seems to be just a guideline, matter of
documentation. Am I mistaken?
I see you haven't stopped doing that.  Stop misstating my point then
arguing against the misstatement.

I am not advocating prohibiting inheritance per se.  Stop presenting
that as if I were.  It's intellectually dishonest.  Let me put that in
simple terms again in case you don't understand it:


Well, perhaps if you did not ignore my "If you want almost anything to
be final ("Every time you omit 'final'
in a class declaration you should consider carefully") you are in fact
arguing AGAINST inheritance in my opinion" and commented on it, we
could finally get somewhere. (In fact we did, see below before you
reply)
I do not advocate prohibiting inheritance.  I advocate thinking
carefully about whether a class should be heritable, and either
designing the class to be so or else prohibiting it.  There are two
options in what I'm proposing, both based on thinking carefully about
what you will permit.  One is to permit inheritance, and if so, design
the class accordingly.  The alternative is to prohibit inheritance.
Either/or.  Not just one.  I am not against inheritance.  I am against
the abuse of inheritance.

I do not advocate prohibiting inheritance in any blanket way.

Do you understand that now?  Or are you going to misstate my point
again?

See above. By the way, you slightly changed your tone from
'inheritance not preferred' to 'I am against the abuse' - and I can,
more or less, accept that better.
Maybe you should break down and actually read the referenced chapters
in /Effective Java/ to get the complete discussion - again, more
cohesively and comprehensively presented than I have done - instead of
attacking points I'm not making and attributing those points to me.

When I have a chance to get my hands on it, I will.
 
D

Daniel Pitts

Lionel said:
Hi all,

Just wondering what arguments there are out there for making methods
and classes final when they are not expected to be overridden/
extended? Typically I would make them final, but when I'm doing code
reviews I don't raise this as an issue because I see it is relatively
trivial.

Thoughts?
I don't always make classes final, even if they "could" or "should" be.

I usually make classes or methods final when I expect there to be
threading concerns. For isntance if you make an immutable and final
class, you can be sure that all instances of the class are immutable. If
it is *not* final, then subclasses could add mutable state.

Final classes also encourage composition over inheritance ;-)

One thing I try to suggest is: If you make several protected methods
which you intend to be overwritten, consider the strategy pattern
instead, it ends up being more flexible most of the time, allowing you
to mix and match behaviors simply by changing the configuration.

P.S. pretty hard to filter through the crap on this group now :(.
Agreed.
 
J

Joshua Cranmer

What makes you think so? Consider SavedGame<- File. while it is clear
that every SavedGame is_a file you certainly do not want to expose
that fact to clients (if you did they could, let's say, save it
anywhere they like or overwrite contents blindly) while still retain
ability of working with files - sheer code re-use. Would you say that
SavedGame has_a File instead?

is-a has a very specific meaning in terms of OOP. If A is-a B, that
means every instance of class A is also an instance of class B, and,
perhaps the most important part, every operation in class B is valid for
objects of type A (public class Square extends Rectangle fails this part
quite miserably).

My copy of /Effective C++/ would call what you want
"is-implemented-in-terms-of"--which is handled by composition or (in
C++) private inheritance.
 
M

Mike Schilling

Rzeznik said:
Ok, you are right.

At any rate,

What makes you think so? Consider SavedGame <- File. while it is clear
that every SavedGame is_a file you certainly do not want to expose
that fact to clients (if you did they could, let's say, save it
anywhere they like or overwrite contents blindly) while still retain
ability of working with files - sheer code re-use. Would you say that
SavedGame has_a File instead?

You said it yourself:: you don't want to expose to clients that SavedGame is
a File, because this will let them call arbitrary File methods on a saved
game. This you don't want to derive SavedGame from File, or, to say it
another way, there's no is-a relationship. What you would do instead is to
give SavedGame a field that's a File and delegate some of the SavedGame
methods to it (delete, possibly rename, etc.) That's precisely a has-a
relationship.
 
M

Mike Schilling

Andreas said:
What makes a class "designed for inheritence"?

Of course documentation - that would go without saying.
What else? Anything else?

Being very careful about public and protected methods calling other public
and protected methods, so that overriding them selectively doesn't lead to
havoc.
 
R

Rzeźnik

is-a has a very specific meaning in terms of OOP. If A is-a B, that
means every instance of class A is also an instance of class B, and,
perhaps the most important part, every operation in class B is valid for
objects of type A (public class Square extends Rectangle fails this part
quite miserably).

So it is, every operation you can perform on a File is valid when
SavedGame is used as a File. I just restrain SavedGame's clients from
casting back to File. The latter example is specific because you
introduced stronger invariant in the descendant, that is perfectly ok.
If a subclass maintains stronger invariant it logically follows that
it maintains weaker invariant as well. Perhaps what is troubling you
is that not all operations valid for Rectangle remain valid when an
instance of Square is acting as a Rectangle. If so you should weaken
your criteria. You are going to lose nice substitution principle for
sure but I do not believe that inheritance is strictly about
substitution.
My copy of /Effective C++/ would call what you want
"is-implemented-in-terms-of"--which is handled by composition or (in
C++) private inheritance.

Well, the problem is that in Java you cannot inherit implementation
without inheriting interface in a clean fashion. I think this might be
one of reasons that people generally frown on implementation
inheritance.
 
M

Mike Schilling

Rzeznik said:
Well, the problem is that in Java you cannot inherit implementation
without inheriting interface in a clean fashion.

Or at all :)
I think this might be
one of reasons that people generally frown on implementation
inheritance.

In fact, in Java aggregation takes the place of implementation inheritance.
 
R

Rzeźnik

You said it yourself:: you don't want to expose to clients that SavedGame is
a File, because this will let them call arbitrary File methods on a saved
game.  This you don't want to derive SavedGame from File, or, to say it
another way, there's no is-a relationship.

There is, but not exposed. Why do you think it suddenly disappeared?
But you know what I am thinking now, that because it is practically
impossible to achieve this in Java because of its very constrained
inheritance scheme, we should not worry about such cases, they'll
remain too artificial no matter how much we argue.

 What you would do instead is to
give SavedGame a field that's a File and delegate some of the SavedGame
methods to it (delete, possibly rename, etc.)  That's precisely a has-a
relationship.

That's precisely awkward has-a relationship. This code would be
inevitably 'smelly', at least every operation that requires File
context. Consider:
//inheritance version
stream.save(save_game)
//delegation
first you have to define save method on SaveGame
public void save(Stream s) { s.save(this.file); }
and then use it like
save_game.save(s);
 
R

Rzeźnik

Or at all :)


In fact, in Java aggregation takes the place of implementation inheritance.

Well, you can, I suppose, using protected statics but it is hideous.
Or you can live with interface "leak".
 
A

Arved Sandstrom

Rzeźnik said:
Lew, I am sorry to say this, but this does not make any sense. It goes
against the very spirit of OOP. If you write your published APIs in a
way you described then your APIs are useless in the face of design
changes. You can get away with this as long as you write programs for
yourself only, or programs with very short life cycle. Why do you fear
inheritance so much? I know of so called FBC problem, I am well aware
what it means, but it is no argument against inheritance.

I don't think anyone is arguing against inheritance in certain
situations. What matters though is the type of inheritance, and who it
is exposed to. Is it public inheritance or implementation inheritance?
Are we providing abstract classes or concrete classes to inherit from?

In another post I mentioned an app I help maintain where roughly a
quarter of about 2850 core classes are subclasses, or about 660 of them.
With the exception of about 30 custom exception classes (public
inheritance from Exception or subclasses thereof) and about 15 tag
handlers that inherit from Facelets TagHandler, and a tiny number of
other cases, all of the inheritance is internal to the application. Over
90 percent of it in fact. There aren't even five classes from external
libraries that we actually sub-class.

Point being, this is only a medium-sized J2EE app, but it's reasonably
complex. We use several dozen third-party APIs (libraries). And yet
we've seen the need to inherit from only 3 or 4 external classes. Based
on my experience this is not unusual.

IOW, inheritance (all sorts: implementation and public, concrete base
classes and abstract base classes) may get used a fair bit in the
_implementation_ of an application or a library, but that doesn't mean
that it gets used a lot across API boundaries. I certainly have never
seen that APIs expect a lot of it to occur, or that it's considered to
be desirable as a general rule.

Some exceptions to that observation are somewhat ubiquitous -
java.lang.Exception and subclasses as concrete base classes, or
javax.servlet.http.HttpServlet as an abstract base class. This simply
goes to show that _sometimes_ inheritance of a class in a public API is
a reasonable way to get things done.

Other posters have commented on this - there simply are better ways of
making your library extensible than to make it possible for clients to
subclass your classes willy-nilly. At most I can see the argument for
carefully crafted abstract base classes in certain situations - I see
considerably less use for non-final public concrete classes.

AHS
 

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

Members online

Forum statistics

Threads
473,777
Messages
2,569,604
Members
45,222
Latest member
patricajohnson51

Latest Threads

Top