Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Accessing private member via subclass

5 views
Skip to first unread message

Mike Schilling

unread,
Nov 23, 2009, 4:30:57 AM11/23/09
to
I thought I understood Java's access control rules pretty well, but
this case puzzles me.

public abstract class Super
{
private int i;

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

public class Sub extends Super
{
}
Consider the starred line. The field "i" is private to Super and is
being accessed by Super, which seems to me to fit within JLS 6.6.1:

if the member or constructor is declared private, then access
is permitted if and only if it occurs within the body of the top
level class (�7.6) that encloses the declaration of the member
or constructor.

However, trying to compile these classes leads to:

Super.java:7: i has private access in Super
s.i = 2;
^
1 error

in both 1.4.2_09 and 1.6.0_06.

Obviously, the error can be removed by changing the line to

((Super)s).i = 2;

And, just as obviously, the error doesn't actually prevent
encapsulation from being broken. For what it's worth, similar (in
fact, almost identical) C# code compiles with no problems.

Any thoughts about this?

Lothar Kimmeringer

unread,
Nov 23, 2009, 5:04:37 AM11/23/09
to
Mike Schilling wrote:

> public abstract class Super
> {
> private int i;
>
> void method(Sub s)
> {
> s.i = 2; // (*)
> }
> }
>
> public class Sub extends Super
> {
> }

[...]


> However, trying to compile these classes leads to:
>
> Super.java:7: i has private access in Super
> s.i = 2;
> ^
> 1 error
>
> in both 1.4.2_09 and 1.6.0_06.
>
> Obviously, the error can be removed by changing the line to
>
> ((Super)s).i = 2;
>
> And, just as obviously, the error doesn't actually prevent
> encapsulation from being broken. For what it's worth, similar (in
> fact, almost identical) C# code compiles with no problems.
>
> Any thoughts about this?

Peronally I think the decision to break with a compile-error
is good, because otherwise you migth run into problems if you
decide to declare a member "i" in Sub at a later point of time.


Regards, Lothar
--
Lothar Kimmeringer E-Mail: spam...@kimmeringer.de
PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

Always remember: The answer is forty-two, there can only be wrong
questions!

Alessio Stalla

unread,
Nov 23, 2009, 5:54:18 AM11/23/09
to
On Nov 23, 11:04 am, Lothar Kimmeringer <news200...@kimmeringer.de>
wrote:

> Mike Schilling wrote:
> > public abstract class Super
> > {
> >     private int i;
>
> >     void method(Sub s)
> >     {
> >         s.i = 2; // (*)
> >     }
> > }
>
> > public class Sub extends Super
> > {
> > }
> [...]
> > However, trying to compile these classes leads to:
>
> > Super.java:7: i has private access in Super
> >         s.i = 2;
> >          ^
> > 1 error
>
> > in both 1.4.2_09 and 1.6.0_06.
>
> > Obviously, the error can be removed by changing the line to
>
> >     ((Super)s).i = 2;
>
> > And, just as obviously, the error doesn't actually prevent
> > encapsulation from being broken.  For what it's worth, similar (in
> > fact, almost identical) C# code compiles with no problems.
>
> > Any thoughts about this?
>
> Peronally I think the decision to break with a compile-error
> is good, because otherwise you migth run into problems if you
> decide to declare a member "i" in Sub at a later point of time.

You can run into such problems independently on the access level of
the field: it being public or protected would not change that.

I, too, would have expected that piece of code to compile fine.

Alessio

Arved Sandstrom

unread,
Nov 23, 2009, 7:39:17 AM11/23/09
to

I'll assume that this is a "let's explore the edge cases in the
language" test case. :-) Having said that, I don't read that section of
the JLS as pertaining to what you are doing here; you might as well have
a method in Super that does a similar thing to an instance of a class
that is not related to Super at all, and you certainly wouldn't expect
that to work either. IOW, using Sub in that method is muddying the
waters. The key point is that you're trying to access a private member
variable and you're not actually in the class that owns it because of
the "s.i" syntax.

I'm a bit surprised that C# would permit this. How identical is "almost
identical"? :-)

AHS

Lew

unread,
Nov 23, 2009, 9:19:53 AM11/23/09
to
Mike Schilling wrote:
>> I thought I understood Java's access control rules pretty well, but
>> this case puzzles me.
>>
>> public abstract class Super
>> {
>> private int i;
>>
>> void method(Sub s)
>> {
>> s.i = 2; // (*)
>> }
>> }
>>
>> public class Sub extends Super
>> {
>> }
>> Consider the starred line. The field "i" is private to Super and is
>> being accessed by Super, which seems to me to fit within JLS 6.6.1:
>>
>> if the member or constructor is declared private, then access
>> is permitted if and only if it occurs within the body of the top
>> level class (§7.6) that encloses the declaration of the member
>> or constructor.
>>
>> However, trying to compile these classes leads to:
>>
>> Super.java:7: i has private access in Super
>> s.i = 2;
>> ^
>> 1 error
>>
>> in both 1.4.2_09 and 1.6.0_06.
>>
>> Obviously, the error can be removed by changing the line to
>>
>> ((Super)s).i = 2;
>>
>> And, just as obviously, the error doesn't actually prevent
>> encapsulation from being broken. For what it's worth, similar (in

Yes, it does. Obviously.

>> fact, almost identical) C# code compiles with no problems.

Arved Sandstrom wrote:
> I'll assume that this is a "let's explore the edge cases in the
> language" test case. :-) Having said that, I don't read that section of
> the JLS as pertaining to what you are doing here; you might as well have
> a method in Super that does a similar thing to an instance of a class
> that is not related to Super at all, and you certainly wouldn't expect
> that to work either. IOW, using Sub in that method is muddying the
> waters. The key point is that you're trying to access a private member
> variable and you're not actually in the class that owns it because of
> the "s.i" syntax.
>
> I'm a bit surprised that C# would permit this. How identical is "almost
> identical"? :-)

To clarify, the access rules only permit an instance of the class itself or of
an inner class to access the private members. Otherwise they forbid access to
'private' members even to subclasses. Since 'Sub' is not an inner class, it
does not have access to the private members of 'Super'. So 's.i' is illegal.

--
Lew

Michal Kleczek

unread,
Nov 23, 2009, 9:26:46 AM11/23/09
to
Lew wrote:

> Mike Schilling wrote:
>>> I thought I understood Java's access control rules pretty well, but
>>> this case puzzles me.
>>>
>>> public abstract class Super
>>> {
>>> private int i;
>>>
>>> void method(Sub s)
>>> {
>>> s.i = 2; // (*)
>>> }
>>> }
>>>
>>> public class Sub extends Super
>>> {
>>> }

[snip]


> 'private' members even to subclasses. Since 'Sub' is not an inner class,
> it
> does not have access to the private members of 'Super'. So 's.i' is
> illegal.
>

But 's.i' is in the body of 'Super'. So it is legal (or there is another
rule I'm not aware of that makes it illegal)

--
Michal

Patricia Shanahan

unread,
Nov 23, 2009, 10:02:24 AM11/23/09
to

The rule that makes it illegal is the i is not even a member of Sub, and
membership in Sub is needed to make the s.i notation valid.

"Members are either declared in the type, or inherited because they are
accessible members of a superclass or superinterface which are neither
private nor hidden nor overridden (�8.4.8)."

http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.4.3

"If the identifier does not name an accessible member field of type T,
then the field access is undefined and a compile-time error occurs."

http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.11.1

Patricia

Michal Kleczek

unread,
Nov 23, 2009, 10:12:40 AM11/23/09
to
Patricia Shanahan wrote:

Looks like the compiler treats it as a member (but not accessible one):


"Super.java:7: i has private access in Super"

--
Michal

Patricia Shanahan

unread,
Nov 23, 2009, 10:16:00 AM11/23/09
to

I think that is just the compiler trying to be helpful and concise at
the same time. The message perhaps should be "Super.java:7: i is not a
member of Sub because it has private access in Super".

Patricia

Arved Sandstrom

unread,
Nov 23, 2009, 12:02:18 PM11/23/09
to
Patricia Shanahan wrote:
> Michal Kleczek wrote:
>> Patricia Shanahan wrote:
>>
>>> Michal Kleczek wrote:
[ SNIP ]

>>>> But 's.i' is in the body of 'Super'. So it is legal (or there is
>>>> another
>>>> rule I'm not aware of that makes it illegal)
>>>>
>>> The rule that makes it illegal is the i is not even a member of Sub, and
>>> membership in Sub is needed to make the s.i notation valid.
>>>
>>> "Members are either declared in the type, or inherited because they are
>>> accessible members of a superclass or superinterface which are neither
>>> private nor hidden nor overridden (§8.4.8)."
>>>
>>> http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.4.3
>>>
>>> "If the identifier does not name an accessible member field of type T,
>>> then the field access is undefined and a compile-time error occurs."
>>>
>>>
>> http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#15.11.1
>>
>>
>> Looks like the compiler treats it as a member (but not accessible one):
>> "Super.java:7: i has private access in Super"
>>
>
> I think that is just the compiler trying to be helpful and concise at
> the same time. The message perhaps should be "Super.java:7: i is not a
> member of Sub because it has private access in Super".
>
> Patricia

I agree - the message isn't wrong, it's just a tad too abbreviated. In
any case there is no mystery to this situation, and no need to resort to
the JLS - how can you refer to a member variable of Sub that it doesn't
have?

As to C# permitting this I'll have to try an experiment when I get on to
my Windows box.

AHS

Mike Schilling

unread,
Nov 23, 2009, 12:28:31 PM11/23/09
to
Patricia Shanahan wrote:
>>
>
> The rule that makes it illegal is the i is not even a member of Sub,
> and membership in Sub is needed to make the s.i notation valid.
>
> "Members are either declared in the type, or inherited because they
> are accessible members of a superclass or superinterface which are
> neither private nor hidden nor overridden (�8.4.8)."
>
> http://java.sun.com/docs/books/jls/third_edition/html/names.html#6.4.3

That explains it. The analogous statement in C# is:

10.2.1: A class inherits the members of its direct base class.
Inheritance means that a class implicitly contains all members of
its
direct base class, except for the instance constructors,
destructors
and static constructors of the base class.

which explain the difference in behavior.


markspace

unread,
Nov 23, 2009, 12:28:41 PM11/23/09
to
Mike Schilling wrote:

> public abstract class Super
> {
> private int i;
>

> void method(Sub s) // <-- Oops

> Any thoughts about this?


I was pretty mystified by your example until I did a second glance at
the line labeled "Oops". Well, obviously if the type of "s" is NOT the
type that holds the private field "i", you can't access "i" through that
type. I think that should be completely intuitive.

Someone else mentioned that Sub could have a public field "i" added at
some point, in which case you'd (likely) have an erroneously behaving
Super. My thought on realizing this was that C# is defective in this
regard and Java is correct.

Mike Schilling

unread,
Nov 23, 2009, 12:17:43 PM11/23/09
to

"Arved Sandstrom" <dce...@hotmail.com> wrote in message
news:VnvOm.54744$PH1.40660@edtnps82...

Agreed that it looks like a weird thing to do, but it came up in real
life.

> Having said that, I don't read that section of the JLS as pertaining
> to what you are doing here; you might as well have a method in Super
> that does a similar thing to an instance of a class that is not
> related to Super at all, and you certainly wouldn't expect that to
> work either.

Of course not. But that would be an entirely different situation.

> IOW, using Sub in that method is muddying the waters. The key point
> is that you're trying to access a private member variable and you're
> not actually in the class that owns it because of the "s.i" syntax.
>
> I'm a bit surprised that C# would permit this. How identical is
> "almost identical"? :-)

I changed "extends" to ":".


Mike Schilling

unread,
Nov 23, 2009, 12:33:38 PM11/23/09
to
Arved Sandstrom wrote:
> I agree - the message isn't wrong, it's just a tad too abbreviated.
> In
> any case there is no mystery to this situation, and no need to
> resort
> to the JLS - how can you refer to a member variable of Sub that it
> doesn't have?

It took the JLS to explain that private members aren't inherited, as
opposed to being inherited but inaccessible, which seems to be the
case in C# (in C++ too, IIRC.)


Arved Sandstrom

unread,
Nov 23, 2009, 1:34:19 PM11/23/09
to

I don't want to go off on a C# tangent here, but I'm not satisfied. The
C# 3.5 docs (in various places) say things like:

A derived class has access to the public, protected, internal, and
protected internal members of a base class. Even though a derived class
inherits the private members of a base class, it cannot access those
members. However, all those private members are still present in the
derived class and can do the same work they would do in the base class
itself. For example, suppose that a protected base class method accesses
a private field. That field has to be present in the derived class in
order for the inherited base class method to work properly.

and

Private members are accessible only within the body of the class or the
struct in which they are declared.

and

Nested types in the same body can also access those private members.

and

It is a compile-time error to reference a private member outside the
class or the struct in which it is declared.

Your quoted C# language spec snippet does not in fact gainsay any of
these. I do not see how the nearest C# equivalent of what we have here
in Java would compile.

AHS

Mike Schilling

unread,
Nov 23, 2009, 1:47:30 PM11/23/09
to

Unlike in Java, where it doesn't inherit them.

> it cannot access
> those members. However, all those private members are still present
> in the derived class and can do the same work they would do in the
> base class itself. For example, suppose that a protected base class
> method accesses a private field. That field has to be present in the
> derived class in order for the inherited base class method to work
> properly.
> and
>
> Private members are accessible only within the body of the class or
> the struct in which they are declared.

In other words, in "super".

>
> and
>
> Nested types in the same body can also access those private members.
>
> and
>
> It is a compile-time error to reference a private member outside the
> class or the struct in which it is declared.

In other words, in "super".

>
> Your quoted C# language spec snippet does not in fact gainsay any of
> these.

It seems to me that they all agree that the code should compile
without error.

> I do not see how the nearest C# equivalent of what we have here
> in Java would compile.

Here they are: try it for yourself.

public class Sub : Super
{


}
public abstract class Super
{
private int i;

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


Arved Sandstrom

unread,
Nov 23, 2009, 5:14:09 PM11/23/09
to

I didn't doubt you that all this compiles, but I went ahead and played
with this in C# anyhow.

However, despite the fact that this does compile, I'm not so sure that
it should. The main phrase from MS that I keep on coming back to is

"Even though a derived class inherits the private members of a base

class, it cannot access those members." The only way I can reconcile
that statement with the actual compiler behaviour is to assume that MS
considers the above scenario to be the superclass doing the
access...which to me is a bit of a smelly situation.

I'm not going to be exploiting this in my C# coding. I will definitely
strive to avoid it though.

AHS

Mike Schilling

unread,
Nov 23, 2009, 6:46:37 PM11/23/09
to
Arved Sandstrom wrote:
> However, despite the fact that this does compile, I'm not so sure
> that
> it should. The main phrase from MS that I keep on coming back to is
> "Even though a derived class inherits the private members of a base
> class, it cannot access those members." The only way I can reconcile
> that statement with the actual compiler behaviour is to assume that
> MS
> considers the above scenario to be the superclass doing the
> access...which to me is a bit of a smelly situation.
>
> I'm not going to be exploiting this in my C# coding. I will
> definitely
> strive to avoid it though.

I doubt it'll come up. I only ran across it because I was doing
something odd.


Siera Gonsages

unread,
Nov 23, 2009, 7:22:47 PM11/23/09
to
On Nov 24, 1:46 am, "Mike Schilling" <mscottschill...@hotmail.com>
wrote:

Arne Vajhøj

unread,
Nov 23, 2009, 7:58:32 PM11/23/09
to
Mike Schilling wrote:
> I thought I understood Java's access control rules pretty well, but
> this case puzzles me.
>
> public abstract class Super
> {
> private int i;
>
> void method(Sub s)
> {
> s.i = 2; // (*)
> }
> }
>
> public class Sub extends Super
> {
> }

> Any thoughts about this?

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

Arne

Lew

unread,
Nov 23, 2009, 8:47:22 PM11/23/09
to

Arved Sandstrom wrote:
>> I agree - the message isn't wrong, it's just a tad too abbreviated.
>> In
>> any case there is no mystery to this situation, and no need to
>> resort
>> to the JLS - how can you refer to a member variable of Sub that it
>> doesn't have?

Mike Schilling wrote:
> It took the JLS to explain that private members aren't inherited, as
> opposed to being inherited but inaccessible, which seems to be the
> case in C# (in C++ too, IIRC.)

One really fun corner case is an inner class that inherits from its enclosing
class. It does not inherit private members of the enclosing class, but it can
access them.

--
Lew

Michal Kleczek

unread,
Nov 24, 2009, 2:21:30 AM11/24/09
to
markspace wrote:

> Mike Schilling wrote:
>
>> public abstract class Super
>> {
>> private int i;
>>
>> void method(Sub s) // <-- Oops
>
>> Any thoughts about this?
>
>
> I was pretty mystified by your example until I did a second glance at
> the line labeled "Oops". Well, obviously if the type of "s" is NOT the
> type that holds the private field "i", you can't access "i" through that
> type. I think that should be completely intuitive.
>
> Someone else mentioned that Sub could have a public field "i" added at
> some point, in which case you'd (likely) have an erroneously behaving
> Super.

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).

> My thought on realizing this was that C# is defective in this
> regard and Java is correct.

IMHO it is better done in C# - more regular.
In Java things get strange sometimes due to such irregularities. Look at the
following:

public class Super {

private int i;

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

}

This compiles fine in Java - should it?


--
Michal

Alessio Stalla

unread,
Nov 24, 2009, 3:30:30 AM11/24/09
to

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.
But things don't work like that in Java; normally it is S that
controls the accessibility of members. So Java's behavior to me
suggests that the Java compiler, when evaluating access rules, acts as
if fields were actually inherited - or, better, copied - in
subclasses, which clearly is not the case.

Alessio

Arved Sandstrom

unread,
Nov 24, 2009, 7:04:36 AM11/24/09
to
Alessio Stalla wrote:
> On Nov 23, 11:14 pm, Arved Sandstrom <dces...@hotmail.com> wrote:
>> Mike Schilling wrote:
[ SNIP ]

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.
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. 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.

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". 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#.

> But things don't work like that in Java; normally it is S that
> controls the accessibility of members. So Java's behavior to me
> suggests that the Java compiler, when evaluating access rules, acts as
> if fields were actually inherited - or, better, copied - in
> subclasses, which clearly is not the case.
>
> Alessio

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.

So in Java the situation (to me) is quite clear - an instance of class
Sub (in this example) does not have a field "i". Period. That's what the
javac message is telling us.

In C# an instance of class Sub does have a field "i". However, the C#
docs tell us that a derived class cannot access private members of the
base class. The example given (a base class protected method accessing a
base class private field, and we obviously want to be have the protected
method work when accessed through the derived class) is a motivation (in
C#) for having those private members present, if not directly
accessible. In Java we simply have it, for the same example, that the
subclass can use such a protected method and things will work.

AHS

Arved Sandstrom

unread,
Nov 24, 2009, 7:28:24 AM11/24/09
to
Michal Kleczek wrote:
> markspace wrote:
>
>> Mike Schilling wrote:
>>
>>> public abstract class Super
>>> {
>>> private int i;
>>>
>>> void method(Sub s) // <-- Oops
>>> Any thoughts about this?
>>
>> I was pretty mystified by your example until I did a second glance at
>> the line labeled "Oops". Well, obviously if the type of "s" is NOT the
>> type that holds the private field "i", you can't access "i" through that
>> type. I think that should be completely intuitive.
>>
>> Someone else mentioned that Sub could have a public field "i" added at
>> some point, in which case you'd (likely) have an erroneously behaving
>> Super.
>
> 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).
>
>> My thought on realizing this was that C# is defective in this
>> regard and Java is correct.
>
> IMHO it is better done in C# - more regular.

I like a lot of things about C# - I prefer that language to Java - but I
sure don't like *this*. I don't find it "regular" at all.

The C# documentation makes it clear that private member fields are not
accessible to derived classes. This - to me - does not conflict with the
statement that private fields _are_ accessible in the owning class,
because in the example we are _not_ referring to an unadorned "i", we're
referring to "s.i", where "s" is an instance of a derived class. That
"s.i" notation, again to me, is the nub of the whole matter - we are
accessing through the derived class, and it's not supposed to have
access to that field. Regardless of the fact that that access is
happening lexically inside the body of the base class.

> In Java things get strange sometimes due to such irregularities. Look at the
> following:
>
> public class Super {
>
> private int i;
>
> <T extends Super> void m(T s) {
> s.i = 5;
> }
>
> }
>
> This compiles fine in Java - should it?

I believe it should. When using "extends", that reference "s" must
behave as an instance of Super; we can't use or guess at any extended
behaviour of a specific T. For example, if you had a List<? extends
Number>, reading from (accessing) that List gives you a Number.

In this case, being in the body of S, getting or setting a private
member field of S is OK.

AHS

Michal Kleczek

unread,
Nov 24, 2009, 7:36:11 AM11/24/09
to
Arved Sandstrom wrote:

> Michal Kleczek wrote:

>> In Java things get strange sometimes due to such irregularities. Look at
>> the following:
>>
>> public class Super {
>>
>> private int i;
>>
>> <T extends Super> void m(T s) {
>> s.i = 5;
>> }
>>
>> }
>>
>> This compiles fine in Java - should it?
>
> I believe it should. When using "extends", that reference "s" must
> behave as an instance of Super; we can't use or guess at any extended
> behaviour of a specific T. For example, if you had a List<? extends
> Number>, reading from (accessing) that List gives you a Number.
>
> In this case, being in the body of S, getting or setting a private
> member field of S is OK.

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?

--
Michal

Arved Sandstrom

unread,
Nov 24, 2009, 8:24:12 AM11/24/09
to
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

Lew

unread,
Nov 24, 2009, 9:13:57 AM11/24/09
to

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.

--
Lew

Alessio Stalla

unread,
Nov 24, 2009, 9:50:29 AM11/24/09
to

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).

> > But things don't work like that in Java; normally it is S that
> > controls the accessibility of members. So Java's behavior to me
> > suggests that the Java compiler, when evaluating access rules, acts as
> > if fields were actually inherited - or, better, copied - in
> > subclasses, which clearly is not the case.
>
> > Alessio
>
> 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

markspace

unread,
Nov 24, 2009, 10:08:58 AM11/24/09
to
Michal Kleczek wrote:

> 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.

Michal Kleczek

unread,
Nov 24, 2009, 10:13:20 AM11/24/09
to
markspace wrote:

> Michal Kleczek wrote:
>
>> 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.

I understand the problem - what I'm saying is that it doesn't have anything
to do with 'i' being private in Super.

Just change 'private int i' in Super to 'public int i'
Now it compiles fine with or without 'i' declared in Sub.
Which 'i' is accessed?

--
Michal

markspace

unread,
Nov 24, 2009, 10:38:57 AM11/24/09
to
Michal Kleczek wrote:

> 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.

Mike Schilling

unread,
Nov 25, 2009, 2:16:16 PM11/25/09
to

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.


0 new messages