"Bob Weiner" <bo*@engr.uconn.edu> wrote in message
news:%2****************@TK2MSFTNGP14.phx.gbl...
Not the answer I was expecting but it is good to hear. I thought I was
missing something big.
Hi Bob.
I think you are missing something big. Something enormous.
I'm thinking about performance and versioning.
This entire topic is why most Delphites think of C# and .NET as a
Delphi-derived platform and not a Java rip-off. The only thing .NET has in
common with Java is the Object class. Delphites understand .NET from day one
while Javaites have some learning to do. Anders Hjelsberg choosed to realize
the default-non-virtual model from Delphi into .NET.
Consider Java. Java methods comes in two flawors; (implicit) virtual and
(explicit) final. Hence all methods are virtual if you don't mark them as
final. This is why Java has no 'override' keyword. If a subclass introduces
the method Foo() and that method exists in any superclass you override it.
Period.
This is dangerous when it comes to versioning. I might give you a class 1.0
with 10 methods and you choose to subclass it with 5 methods. Then I release
a 1.1 version of my cool and useful class, but I've added 3 methods and one
of them has the same name as one of yours.
Without your knowledge you have just overidden a method. As there are no
explicit overriding mechanism in Java, the compiler will happily compile and
override. The problems and obscure bugs from this scheme is two-folded. Your
method is now part of a polymorphic behaviour without support for it. When
my code calls my method your method gets called. Your method will
(obviously) not call on super() and hence my important code will not get
executed. My method might change the object state and then assume that some
actions where committed (that was not executed). Do you see the problem?
This is a serious design-flaw in the Java language and this is why we see so
little inheritance in Java. Javaites tend to use final classes that derives
from object or beans in conjunction with interfaces to protect themselves
from these kind of unwanted overriding.
Also, virtual methods are not good for performance. A virtual method cannot
be statically linked. Whenever you call on a virtual method you have a level
of indirection as the runtime has to check what type the object really is
and call it's method. More on this below.
Delphi and .NET have solved this problem gracefully by including a third
mode that is default. Non-virtual methods. With non-virtual methods we are
no longer afraid of subclassing. Just compare TForm in Delphi 1.0 and Delphi
7.0 and you can see that Borland is not afraid of having many superclasses
and adding a lot of stuff to them. Same is true for .NET as it is nothing
but a rip-off from VCL and WFC. Not sure if it can be called a rip-off as
Hjelsberg was involved in all three of them.
Anyways, as methods are non-virtual by default the compiler can statically
link a method at compile-time. This is done by the JITer. An example:
MySuperClass o = new MySubClass();
o.DoStuff(); // method is non-virtual and the method in MySuperClass will be
called without hesitation. This is fast.
o.DoMyOverriddenStuff() //method is virtual and runtime have to check and
realize that o is typeof MySubClass and call the correct method. This hurts.
Now for some funny facts. The jitter in Java actually do support non-virtual
methods. The java runtime marks all leaf-classes (classes that have no
subclasses) and call on methods non-virtual. This is why the java runtime is
so much faster these days compared to older runtimes. The difference is that
..NET sport non-virttuality in the language while Java does not.
Now for your question about why you have to reintroduce non-virtual methods.
This is just to protect you from the flaws in Java. Virtual methods can be
overriden or reintroduced. Non-virtual methods can only be reintroduced.
Delphi actually uses the keyword 'reintroduce' while C# reuses the keyword
'new'. If you fail to do so; in Delphi you get a warning, in C# you get an
compile error. I like the latter.
This is a great safety-net. Whenever a subclass sports the same method as a
method in a superclass you cannot compile until you accept that. You can
reintroduce the method and if it's virtual you can override it. But the
great thing is that you have to respond and make a choice.
Then what is the correct choice?
Well, I think that reintroducing a method is a bad thing. If a superclass
wanna DoStuff() and your subclass wanna DoStuff() and these thingies are
different things; you don't wanna reintroduce stuff but rename your method
into DoThings().
My conclusion: Never ever reintroduce methods, but love the compiler for
stopping you from unwanted polymorphism and the weird bugs that follows.
Happy Coding
- Michael S