Accessing private member via subclass

A

Arved Sandstrom

Michal said:
But that basically means:
"we can access 'i' here because any value passed to m() will have a type
that is a subtype of Super and i is a member of Super".
On the other hand in the original example we have:
"we cannot access 'i' here because any proper subtype of Super does not
inherit 'i'"

Don't you think it is a contradiction?
I don't think it's a contradiction as things are defined now - generics
have their own rules - but I'm not fond of pushing the envelope like
this. Since the intent of the method is to set the private member field
of the base class to 5, I'd just code it as

i = 5;

and it becomes unambiguous. I think your general objection is valid - we
ought not have to ask questions like this.

My personal belief is that when we run across situations like this is
that (1) sometimes we should not be allowed to do it, and (2) we
shouldn't try to do it anyway. So in this particular case perhaps the
"s.i" should be illegal.

AHS
 
L

Lew

Michal said:
But that basically means:
"we can access 'i' here because any value passed to m() will have a type
that is a subtype of Super and i is a member of Super".
On the other hand in the original example we have:
"we cannot access 'i' here because any proper subtype of Super does not
inherit 'i'"

Don't you think it is a contradiction?

No more so than that casting to 'Super' allows access. In other words, no -
it's entirely consistent. You can think of 'T extends Super' as casting to
'Super' whatever 'T' really is.

It would be a contradiction if ((Super) T).i were legal but the <T extends
Super> example were not.
 
A

Alessio Stalla

[ 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
 
M

markspace

Michal said:
But it does not have anything to do with 'i' being accessible or not. The
issue would remain if you change 'i' to public in the original example (and
it will compile fine).


I don't think we're saying the same thing. Here's the OP's example,
with one addition. This is what I'm talking about:

public abstract class Super
{
private int i;

void method(Sub s)
{
s.i = 2; // (*)
}
}

public class Sub extends Super
{
public int i; // ADDED THIS LINE!
}

Now, which "i" does C# access? In Java, the compiler forces you to
cast, which means that the "i" in Super will always be accessed. In C#,
I'm not sure. I think C# would switch from Super to Sub, which almost
certainly would introduce a bug into the code.

public class Super {

private int i;

<T extends Super> void m(T s) {
s.i = 5;
}

}

This compiles fine in Java - should it?


Because of type erasure, the code above actually executes as:

public class Super {

private int i;

void m(Super s) {
s.i = 5;
}

}

And if you deal with generics often, it's pretty obvious this is what
it's doing. <T extends Super> is a check done at compile time, not
runtime. So I don't have a problem with this. It can be confusing for
folks who are new or new to generics, I'll admit.
 
M

markspace

Michal said:
Just change 'private int i' in Super to 'public int i'


Assume it's a design constraint that "i" in Super be private. The
designer wants that field encapsulated and therefore private.

Now it compiles fine with or without 'i' declared in Sub.
Which 'i' is accessed?


I'm concerned about modifications after the initial design.

What is the cost of maintaining this code?

After the code is in maintenance, a programmer decides to modify "Sub"
or create a new class "Sub2 extends Super" and unwisely decides to add a
public field "i" because he or she is unaware of the initial designs'
use of the private field "i" in Super.

Have we just introduced a bug into the code? Is this therefore higher
cost than the Java version, which requires that we specify which "i" we
want in the initial design?

I think the answer is answer is "yes" to both latter questions but since
I haven't got an answer to my question about C# I'm still not 100% sure.
 
M

Mike Schilling

Arne said:
Given that Super class should not refer to Sub class,
then this seems to be an intellectual exercise.

As I said upthread, it came up during normal development. (For some value
of "normal".)

I have some code that generates Java classes from XMLSchema types. One of
its features is that the generated classes can be customized. If the
generator, which is about to create XXX.java, notices that there's a
hand-coded XXX.java, it instead generates an abstract base class
XXX_Base.java.
(The handcoded XXX is expected to anticipate this and extend XXX_Base).
Now, if the generated XXX_Base includes a factory method, it will of course
create XXX's (since you can't create XXX_Base's, which are abstract), and
might decide to mess with their private parts.

Having run into this isue, it was trivial to generate

XXX_Base instance = new XXX();

instead of

XXX instance = new XXX();

but, as I said, I was at first puzzled by the need to do this.
 

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

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,065
Latest member
OrderGreenAcreCBD

Latest Threads

Top