why is a superclass allowed to access protected methods of a subclass?

P

P.Hill

Pat said:
"why does java allow a superclass to access protected overridden methods on
an instance of a subclass,
even when the subclass is in a diffrent package?".

I hadn't been following this thread closely, because I kept thinking the
behavior was what I expected, but your restatement seems weird. Why
would you ask this question? What is the problem you preceive?

If there is a superclass in the animal package called Animal with a method
called walk() and over in the quadraped package you have implementations
of Animal like Bear.walk() and Horse.walk() I would hope you
would not expect there to be any rational reason that a method in
the animal package couldn't call walk() or any other protected method
on any class which implements the method.

Yes members of the same package are somewhat similar to C++ friends.

-Paul
4) a number of people have explained/justified the behaviour in terms of
polymorphim. I disagree - in my opinion, - encapsulation is a compile time
issue - its about the compiler enforcing rule of visibility - so it's
determined by the object's static type and its package. Polymorphism is
purely a run-time thing - not the realm of the compiler at all. It seems to
me that fact that the compiler allows access to a protected method on an
object outside the pacakge is an odd exception to the general rule, and the
fact that the subtype is a related type is not a strong enough reason to
justify it.

The protected qualifier the caller is coming in by is the one on the superclass
in the same package. The implementation happens to be provided by some other
class which seems irrelevant to me. can something in animal call an
implementation of Animal.walk()? Yes, Animal.walk() is accessable to
things the animal package.

-Paul
 
P

Pat Ryan

The behavior I expected is that
(i) protected methods are not part of the externally visible interface of a
class, but are only visible to "friends" of the class (i.e. classes in the
same package)
and
(ii) protected methods are *internally* visible within the class itself and
its subclasses.

So a subclass can always call an inherited protected method internally - its
calling a method on itself.

But at runtime an object can never access the protected methods of another
object which is not
a friend. To take your example, if doIt() is a method on Animal, Bear is a
subclass of Animal in a different package, and walk is a protected method on
Animal :

public void doIt() {
Bear aBear = new Bear();
aBear.walk(); // illegal, walk is a protected method of an object which is
not our friend
}

C++ wont let you do this (call aBear().walk), and neither will java, after
the compiler bug was fixed in 1.4.

Why is this an issue?

I can think of two reasons
(i) it is counter-intuitive -as I stated originally it is an arbitrary
violation of encapsulation.
(ii) it increases coupling. Now if the protected method of the subclass is
changed, the effects of the change
can ripple up the hierarchy, to superclasses, as well as down, to
subclasses. This is a bad thing.
 
J

John C. Bollinger

Pat said:
The behavior I expected is that
(i) protected methods are not part of the externally visible interface of a
class, but are only visible to "friends" of the class (i.e. classes in the
same package)
and
(ii) protected methods are *internally* visible within the class itself and
its subclasses.

So a subclass can always call an inherited protected method internally - its
calling a method on itself.

I don't usually find it useful to divide a class' interface quite that
way. If I want to make just two categories, then I will invariably lump
in protected members with public ones, because they can _always_ be
_made_ accessible to any class via a trivial subclass. Where the
default-access members should go depends on the purpose of the
categorization.

Anyway, the semantics you describe are good, just, and right as far as
they go. They don't, however, go into the questions of polymorphism and
virtual method dispatch, which complicate the issue.
But at runtime an object can never access the protected methods of another
object which is not
a friend. To take your example, if doIt() is a method on Animal, Bear is a
subclass of Animal in a different package, and walk is a protected method on
Animal :

public void doIt() {
Bear aBear = new Bear();
aBear.walk(); // illegal, walk is a protected method of an object which is
not our friend
}

C++ wont let you do this (call aBear().walk), and neither will java, after
the compiler bug was fixed in 1.4.

But Java will still let you do this:

((Animal) aBear).walk();

which has the same result. (That is, Bear.walk() is selected for
invocation at runtime.) I'm not a C++ expert; what would C++ do with that?

That's not a peculiar case, either; it applies anywhere that the walk()
method is invoked on an expression of _type_ reference to Animal,
regardless of the runtime _class_ of the actual referrent and whether
walk() is overridden in that class. The point is that "access" is a
tricky word in a situation like this, and at least part of its relevance
is indeed restricted to compile time, as Paul Hill wrote. And
furthermore, it applies to every access level except private (private
methods are not subject to virtual dispatch).

That's also why the observed buggy behavior in fact makes a certain
amount of sense -- the java compiler was in effect applying an implicit
upcast of the invocation target expression, resulting in a legal method
invocation. The only problem there is that it is not actually allowed
by the Java language. Oops.
Why is this an issue?

I can think of two reasons
(i) it is counter-intuitive -as I stated originally it is an arbitrary
violation of encapsulation.

I'll agree with that, to some extent. However, any class that can
access some particular method on the superclass can in fact access all
subclasses' overriding versions, if perhaps only indirectly. This is a
feature of polymorphic objects. Is it different in C++? In SmallTalk?
In some other widely-used OO language? (Really, I'm curious.)
(ii) it increases coupling. Now if the protected method of the subclass is
changed, the effects of the change
can ripple up the hierarchy, to superclasses, as well as down, to
subclasses. This is a bad thing.

I think you're missing it here, though. The kind of effect you describe
is an inherent feature of any language that offers polymorphic objects.
Your particular examples does exhibit a coupling problem, in that a
superclass has a dependency on one of its subclasses (allowed in Java),
but I think that's largely unrelated to the method access question we're
discussing.


John Bollinger
(e-mail address removed)
 
P

P.Hill

John said:
But Java will still let you do this:

((Animal) aBear).walk();

But your casting suggests that walk() exists in Animal, so
if Bear is a particular instance of Animal as you say
then Animal.walk() has got something as its implementation
and that implementation might be the overridden version
of walk() from Bear.
which has the same result. (That is, Bear.walk() is selected for
invocation at runtime.)

Doesn't that make semantic sense? It does to me.
The point is that "access" is a
tricky word in a situation like this, and at least part of its relevance
is indeed restricted to compile time, as Paul Hill wrote.

I think someone could make the argument that this is not a bug but
a feature! You make Bears waddle instead of walk and every thing
that makes a bear.walk will see the change... as it should be.
The kind of effect you describe
is an inherent feature of any language that offers polymorphic objects.

That is what I'm thinken' too.
-Paul
 
S

Sudsy

P.Hill said:
But your casting suggests that walk() exists in Animal, so
if Bear is a particular instance of Animal as you say
then Animal.walk() has got something as its implementation
and that implementation might be the overridden version
of walk() from Bear.

But walk could be declare in Animal as abstract.
 
P

P.Hill

Sudsy said:
But walk could be declare in Animal as abstract.

So? The animal will walk, because the implemention
of the particularly instance has a walk() method.

You actually can't get an instance without a walk()
method.

-Paul
 
P

Pat Ryan

P.Hill said:
But your casting suggests that walk() exists in Animal, so
if Bear is a particular instance of Animal as you say
then Animal.walk() has got something as its implementation
and that implementation might be the overridden version
of walk() from Bear.


Doesn't that make semantic sense? It does to me.

I suspect we may be misunderstanding each other here. It was never my
intention to suggest that a superclass should never have access protected
methods of subclass. If it is done via an upcast, that is no problem - you
have to be able to do that to support polymorphism. I just object to it
being done directly, without casting, because that breaks encapuslation.
 
P

Pat Ryan

John C. Bollinger said:
I'll agree with that, to some extent. However, any class that can
access some particular method on the superclass can in fact access all
subclasses' overriding versions, if perhaps only indirectly. This is a
feature of polymorphic objects. Is it different in C++? In SmallTalk?
In some other widely-used OO language? (Really, I'm curious.)

The other OO languages I've used are C++ and Object Pascal. Both support
polymorphism through upcasting.
It is pretty fundamental I think. My C++ is a little rusty but a colleague
of mine tried the problem outlined in the OP and confirmed that C++ does not
allow a superclass to directly access protected methods on an instance of a
subclass, but it does allow this via a cast. I don't have access to a Delphi
compiler at the moment but I'd be very surptised if it didn't behave in a
similar fashion.

Here is the problem expressed in C++, witha little more detail (the
commented out lines cause compiler errors)

#include "stdio.h"


class A
{
public:
A(int instance) { m_instance = instance; }
void apub();

int m_instance;

protected:
virtual void ado() { printf("A.ado() invoked %d\n", m_instance); }
virtual void over() { printf("A.over() invoked %d\n", m_instance); }
};



class B : public A
{
public:
B(int instance) : A(instance) {}
void bpub();

protected:
virtual void bdo() { printf("B.bdo() invoked %d\n", m_instance); }
virtual void over() { printf("B.over() invoked %d\n", m_instance); }
};



void A::apub()
{
A* ap = new A(1);
ap->ado();
ap->over();

B* bp = new B(2);
((A*)bp)->over();

A a(3);
a.ado();
a.over();

B b(4);
//b.bdo(); // 'B::bdo' : cannot access protected member declared in
class 'B'
//b.over(); // 'B::eek:ver' : cannot access protected member declared in
class 'B'
((A)b).over();
}


void B::bpub()
{
// A a;
// a.ado(); // 'A::ado' : cannot access protected member declared in class
'A'
// a.over(); // 'A::eek:ver' : cannot access protected member declared in
class 'A'

}




This outputs



A.ado() invoked 1

A.over() invoked 1

B.over() invoked 2

A.ado() invoked 3

A.over() invoked 3

B.bdo() invoked 4

B.over() invoked 4

A.over() invoked 4

Which is pretty much the same behaviour as java 1.4. I was surpised at the
final linal of output tho - when we upcast a pointer we get the overridden
method (illustrated by the third line of output), but when we upcast an
object we get the superclass method (illustrated by the final line of
output). Doesn't seem right to me.


I think you're missing it here, though. The kind of effect you
describe
is an inherent feature of any language that offers polymorphic objects.
Your particular examples does exhibit a coupling problem, in that a
superclass has a dependency on one of its subclasses (allowed in Java),
but I think that's largely unrelated to the method access question we're
discussing.


Ok - I guess thats a fair cop. Somehow though I still feel that a class
which calls a protected method of another class directly is more strongly
coupled than a class which accesses the protected method by upcasting or
subclassing. But I'm struggling to find a concrete example to justify this.
 
W

Woebegone

Chris,

Thanks very much for your patient and thorough explanation. The community
can be thankful I wasn't consulted during the 1.4 implementation!

Sean.
 

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

Forum statistics

Threads
474,261
Messages
2,571,040
Members
48,769
Latest member
Clifft

Latest Threads

Top