Virtual by default or not?
I've been skimming through “Hardcore Java”, and I came across a section on the use of 'final' in Java. In it, one of Simmons' comments (okay, I'm not sure it's his comment because he's listed as editor and not author, but that sounds better than “whoever wrote this section) is (and I'm paraphrasing here):
Don't use final on methods unless you're sure you want them to be final
He then goes on to say that this is because you never know who might want to extend your class.
There are really two viewpoints on this issue. They are:
“Make it virtual in case somebody wants to extend it”
and
“Don't make it virtual unless you're sure somebody wants to extend it”
I'd like to expand on the first viewpoint, which I've sometimes labelled as “speculative virtuality”. I don't like it.
My reasons have everything to do with predictability and robustness. If you can't conceive of a user extending your class in a specific way but still choose to provide such extensibility, then it's pretty clear that you don't have an extension scenario in mind, which means that neither your code nor your tests are likely to ensure that it does work - especially across versions. Sure, it's possible that it may work in the current version, and may even continue to work in future versions, but I wouldn't call it a supported scenario. I'd prefer to use classes where I know what I'm doing is supported.
The second issue is around understandability. If I walk up to a class (in the metaphorical sense, of course, you can't really “walk up” to a class) and it has 29 virtual methods, it's hard for me to tell whether the author *intended* me to extend the class through a certain method or set of methods, or whether they just left them virtual “just in case”. Where if a class only has one (or a small number) of virtual methods, that's a good indication that the designer wants me to use them (ie they're part of a supported scenario).
Comments
Anonymous
May 17, 2004
Regarding "Speculative Virtuality" Josh Bloch doesn't seem to like it either in "Effective Java." Specifically #15: "Design and document for inheritance or else prohibit it" It was also mentioned in an interview at http://artima.com/intv/blochP.html
MattAnonymous
May 17, 2004
As a counterpoint, there are many times I'll walk up to a class and find out that it does everything I want but one method is incorrectly implemented (or just not implemented in the way I need).
Often in that case, I can look at the source using Reflector and carefully override that one method. Unless it's not virtual. The ideal solution would be to have the author fix the problem, but that's not always done in a reasonable timescale.Anonymous
May 17, 2004
So it comes down to control. Eric seems to be inclined to dictating the end user what they're allowed to do and what not. As oposed to opening up and letting the user do whatever they want, including shooting themselves in the foot (which I prefer, because most of the time you have really no idea who your end users are going to be and what their needs are).Anonymous
May 17, 2004
I think this viewpoint is probably caused by a fairly large cultural difference between Java (especially earlier on) and .NET. Since in Java "everything is virtual" by default, this Java design decision is often taken for granted and not questioned by native developers. Josh Bloch's and other publications took a great step toward changing this mentality, and in general the Java community has really gone in the direction of composition and interface/delegation instead of inheritance for everything. However, there still exists quite a strong feeling that every type should remain open to what you refer to as "speculative virtuality."
Coming from a Java background, I was stunned and perplexed when I first learned it was necessary to opt-in to being virtual in C#. Almost heresy, in fact. Why? Not because I thought inheritance was the extensibility panacea, but rather because I was in the habit of saying when I didn't want virtuality versus saying when I did. It sounds like a pretty minor difference, but it's actually very fundamental and hardwired into the platform developer's thinking.
I still haven't made up my mind entirely about this one (similar to checked exceptions), but I do see convincing arguments both ways.Anonymous
May 17, 2004
I totally agree with you, but allow me to play Devil's advocate and ask: Why then are C# classes not "sealed" by default? In other words the "sealed" keyword should really be "extensible" and you should have to explicitly choose for your class to be a base class just like you should choose which methods should be virtual. :)Anonymous
May 17, 2004
Sure enough, your idea that as little as possible should be virtual has been well applied in large areas of the .net framework.
Unfortenately, this often makes it quite hard to do some things you'd want to do. This is especially true for rather "generic" areas like the asp.net infrastructure that could be used to do very many cool things the guys in charge at MS probably did not think about (talking about multiple-language pages, for instance), if only you could get a little more control.
If many cases, you have to go and re-do quite a bit of infrastructure (thanks to Reflector for help on this...) because you want to customize one little thing...
So maybe it should be 'virtual only if it makes sense', but in a somewhat more generous way?Anonymous
May 17, 2004
I quite the Java route liked because I wasnt relying on the muppets to remember to virtual something.
But then on the flip side, would I want to use the muppet developed class.Anonymous
May 17, 2004
This assumes you know every future needs of your clients. As far as I know, nobody has achieved that.
Even if there are many virtual methods, should I override them just because I can?
I have posted this question numerous times and are still waiting for a good answer: How do I design for extention when I don't know how my future client will use my class?
The User Interface Application Block is a good example where classes are closed for extention when they shouldn't. Classes are sealed or internal, methods which should be virtual are not. This means that the only way I can adjust the framework to my needs is to edit the source code. I don't want to do that, because my changes will never survice the next framework release.Anonymous
May 17, 2004
The comment has been removedAnonymous
May 18, 2004
The comment has been removedAnonymous
May 18, 2004
So how, exactly, do you design something to be extendable? How do you know when something is not extendable? Just because you don't need to override something doesn't mean I don't. I can take your class and see usages you never thought of. Who are you to tell me that my prefered implementation is flawed? I know what I need, you don't. I can use Reflector to inspect your code and see it is perfectly safe to override, even if you made the method inaccessible.
Just because you can't see the need to override doesn't mean it is unsafe to do so.Anonymous
May 18, 2004
The comment has been removedAnonymous
May 19, 2004
"Don't be a moron. Think about what you are saying. Clearly this is 'hacking', not 'software engineering'."
No. It's inspection to see what's really going on. This way I can learn more about your implementation and see what's safe and what's not.
I also design my classes for a purpose. But in contrast to you, I think it's wonderful when someone find a usage scenario I didn't think of.
We have also seen examples of when a class should be open for extention but is not. Isn't that an oo principle: Closed for modification, but open for extension?
I am glad we don't have it completely your way, then most would be closed for anything.
Funny, this. Before .Net everybody argued how bad VB6 was, no inheritance and all. Now the argument is how bad oo is, and how we should be protected from ourselves.Anonymous
May 19, 2004
The comment has been removedAnonymous
May 19, 2004
The comment has been removedAnonymous
May 19, 2004
Well not to mention the fact that virtual methods can't be inlined. :)Anonymous
May 19, 2004
Virtual methods can be inlined if you can prove the "Single Subclass assumption". (The optimization has to be reversed if a class is loaded that changes that assumpiton), or you can make a guarded inline if the vast majority of the calls are going to one or a few distinct classes.
Most JVM's today do that, (probably necessary because of the "virtual by default" language choise.)
In general, I find the CLR (v1.1 at least) to be lacking in inlining cababillities, and it also produce less efficient code than for example the JRockit JVM.Anonymous
May 29, 2004
<i>My reasons have everything to do with predictability and robustness. If you can't conceive of a user extending your class in a specific way but still choose to provide such extensibility, then it's pretty clear that you don't have an extension scenario in mind, which means that neither your code nor your tests are likely to ensure that it does work - especially across versions
</i>
Even if you can't imagine a situation where you would want to override, it's still possible to write your code in such a way that the virtual methods (which by their nature are public) are sufficiently black boxed that an overriden version won't be a problem. Just because you find it hard to imagine a use for overriding a method doesn't mean that me or other users of your class can't.
I'l like to see your response to why C# doesn't have classes "sealed" by default or why most classes in the .NET BCL aren't sealed.Anonymous
June 24, 2004
My 2 cents on the Sealed by default argument.
They appear similar in concept but to me its about making your intentions as a class designer clear: what your saying when you declare a virtual? and compare that to the statement your making when you declare a class sealed.
By declaring a virtual you are stating a granular level of behaviour in a very specific way. This is different to what you state when using the same concept at class level.
If you could declare a class 'extensible' you could be implying that the class as a whole is implicitly overridable - which clearly isn't the case in C# (this of course is different to Java). Furthermore you are stating your extensibility level twice (if method A is virtual then the class X must also be extensible).
By having a class explicitly declared as sealed you imply that the class is a final implementation and cannot be extended (which is true). You aren't doubling your extensibility declaration and your intentions are therefore made clear to the user of your class.
That's my understanding of the default behaviour.Anonymous
December 18, 2004
Helpful For MBA Fans.Anonymous
June 18, 2008
Few keywords are as simple yet amazingly powerful as virtual in C# (overridable in VB.NET). When youAnonymous
June 18, 2008
Few keywords are as simple yet amazingly powerful as virtual in C# (overridable in VB.NET). When you