[ SNIP ]
Why is it smelly? To me it seems the most natural interpretation, and
I'm surprised about Java's behaviour (mind that I routinely program in
Java and have done very little C#).
Consider a generic expression object.field where object is of type T.
Suppose the expression is textually contained in a method declared in
class S. If you argue that the access to the field is done by
'object', rather than by S, then the visibility of object's type T
would apply, and you would have access to all of T's members, since T
can always "see" them. In other words, there would be no encapsulation
whatsoever for members declared in T.
It might be more accurate to say that I consider the entire example to
be smelly (no reflection on Mike's experimenting here)...there's nothing
good about having a method in a superclass that calls out a subclass.
Ok, agreed.
But it's legal code (in C#, but not in Java) so it's worthy of academic
discussion.
To use your terminology, I expect "i" to be accessible only in S. In
both languages. That is to say, in the body of class T we cannot access
S's private field "i". So far so good.
So far so good, yes.
However, and again in both
languages, I don't expect an instance of T to behave like its base class
S just because a base class private member is accessed in the lexical
scope of S. This is not intuitive to me at all. I would expect one to
have to cast the instance to the base class, just like Mike demonstrated
in the case of Java.
Yet for protected it works exactly like that:
public class Super {
protected Object foo;
public void bar(Sub sub) {
sub.foo = null;
}
}
public class Sub extends Super {}
In general sub.foo is illegal, but within Super it is not
(independently of whether Sub and Super are in the same package).
I'm certainly arguing that object.field access is done by object type.
That's how it works. Given the "object.field" expression, and object
being of class T, my ability to see "field" outside the body of T
depends on its existence and then its access modifier. In Java's case T
doesn't even have a field "i".
It is this fact that surprises me, yet I must of course accept it.
However, I think this is counterintuitive since inheritance works
differently for non-private members. I suppose they did it because
inheritance just has to work differently for private methods - you
must not be allowed to override a private method, or the
implementation details of the superclass will potentially leak to
subclasses (possibly without they being aware of it!) - and for
symmetry they used the same rule for private fields as well.
In C#'s case it does, but in this
particular situation I don't expect an automatic cast to the base class
S just because we are in the body of S...however, that's what we seem to
get in the case of C#.
If in C# Sub has the field, no cast is necessary to access it, not
even an implicit one. I wonder what C# does with private methods at
this point (see above).
Well, in Java, public and protected (and in some cases package-private)
members of the base class *are* inherited by the subclass. This is
unambiguous in all documentation. The difference between C# and Java in
this case lies in private members - a Java subclass does not inherit
them at all; a C# subclass does, it just cannot access them.
Hmm now I think I understand that... it seems to me that the only
distinction between not inheriting and inheriting but not being able
to access is precisely the OP's example.
Alessio