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

P

Pat Ryan

I find it surprising that java allows a super class to access the protected
methods of an instance of its subclass, even if the subclass is in a
different package (as illustrated by the code snippet below)
The converse is not true - i.e. a subclass can't access the protected
methods of an instance of the superclass, unless it is in the same package.

To me this seems like a rather arbitrary violation of encapsulation.
I dont think other OO languages I've used (C++ and Delphi), support this
behaviour.


--- in file A.java ------------
package a;
import b.B;

public class A {
protected void doIt(){
B b = new B();
b.doIt(); // no problem - superclass can access protected methods of
subclass
}
}

--- in file B.java ---
package b;
import a.A;

public class B extends A{
protected void doIt() {
\A a = new A();
a.doIt(); // won't compile subclass can't access protected methods of an
instance of superclass
}
}
 
J

Jose Rubio

Pat Ryan said:
I find it surprising that java allows a super class to access the protected
methods of an instance of its subclass, even if the subclass is in a
different package (as illustrated by the code snippet below)
The converse is not true - i.e. a subclass can't access the protected
methods of an instance of the superclass, unless it is in the same package.

To me this seems like a rather arbitrary violation of encapsulation.
I dont think other OO languages I've used (C++ and Delphi), support this
behaviour.
Protected in Java means that every class in the same package has access to
it. In other words is package public. True, it's is a deviation from C++,
but Java does a better job with handling package scopes than C++ does with
using namespaces.
--- in file A.java ------------
package a;
import b.B;

public class A {
protected void doIt(){
B b = new B();
b.doIt(); // no problem - superclass can access protected methods of
subclass
}
}
I tried this and I get a compilation error, well actually I tried caling a
method with a different name, but if you call it with the same name like you
are doing it, it does compiles. I'll have to look at this deeper.
--- in file B.java ---
package b;
import a.A;

public class B extends A{
protected void doIt() {
\A a = new A();
a.doIt(); // won't compile subclass can't access protected methods of an
instance of superclass
}
}
If you call doIt() without creating the instance it will work. When you call
it through the instance you are not using the inhereted method, you are
calling it just like any other object would.
 
J

Jose Rubio

Here's a more detailed answer to your question (actually the code is the
best answer!!) In summary, when both the Parent and Child have a method with
the same signature, with both in different packages, you are really
accessing the paren't method. That's why it compiles, but you don't have
access to the child's method.


Hope it helps.

Jose

package FirstGeneration;

public class Parent

{

public void testParent()

{

FirstGeneration.Child homeBoy = new FirstGeneration.Child();


SecondGeneration.Child collegeKid = new SecondGeneration.Child();


//This I can do because I'm accessing my own method,

//not the one overwritten by the subclass,

//so there is no violation, remember a Child is a Parent

//in this case

collegeKid.superClass();


//Here I'm actually accessing the child's superClass

//because it's visible since he's in my package

homeBoy.superClass();


//This one is not allowed, because you can't access the

//protected method outside the package

//collegeKid.subClass();


//But this one is since he's in the same package

homeBoy.subClass();


}

protected void superClass()

{

System.out.println( "I'm in superClass of Parent");

}

}

package FirstGeneration;

public class Child extends Parent

{

public void testChild()

{

//Child's superClass method

superClass();


//Parent's superClass method

super.superClass();

}


protected void subClass()

{

System.out.println("I'm in protected subClass of child");

}


protected void superClass()

{

System.out.println("I'm in protected superClass of child");

}

}

package FirstGeneration;

import SecondGeneration.Child;

public class Uncle

{

public void test()

{


Parent heIsReallyMyBrother = new Parent();


//I can access the superClass method of my brother

//because he's in my package

heIsReallyMyBrother.superClass();


Child myNephew = new Child();


//I can't access the subClass method because it's

//not visible outside the package

//myNephew.subClass();


//The superClass method I can access because I'm accessing the Parent method

//not the Child one. Remember a Child is a Parent (even though in real life

//it doesn't make sense :)

myNephew.superClass();

}

}

package SecondGeneration;

import FirstGeneration.Parent;

public class Child extends Parent

{

public void testChild()

{

//Child's superClass method

superClass();


//Parent's superClass method

super.superClass();

}


protected void subClass()

{

System.out.println("I'm in protected subClass of child");

}


protected void superClass()

{

System.out.println("I'm in protected superClass of child");

}

}
 
M

Matt Humphrey

Pat Ryan said:
I find it surprising that java allows a super class to access the protected
methods of an instance of its subclass, even if the subclass is in a
different package (as illustrated by the code snippet below)
The converse is not true - i.e. a subclass can't access the protected
methods of an instance of the superclass, unless it is in the same package.

To me this seems like a rather arbitrary violation of encapsulation.
I dont think other OO languages I've used (C++ and Delphi), support this
behaviour.


--- in file A.java ------------
package a;
import b.B;

public class A {
protected void doIt(){
B b = new B();
b.doIt(); // no problem - superclass can access protected methods of
subclass
}
}

--- in file B.java ---
package b;
import a.A;

public class B extends A{
protected void doIt() {
\A a = new A();
a.doIt(); // won't compile subclass can't access protected methods of an
instance of superclass
}
}

This is interesting, but I'm not sure which rules in the JLS apply. Section
6.6.2 says that a protected method may be accessed from outside its package
only by the code responsible for implementing that method. In your example,
if class B had a protected method named otherDoIt (), it would not be
accessible in A. Also, access control cannot be more restrictive in
subclasses. That is, it's not possible for b.doIt to be private.

Also, your statement about a subclass not being able to access a
different-packaged superclasses's protected methods isn't quite true (unless
I've misinterpreted you) because a subclass can invoke super () on protected
methods . In your example, B.doIt () can invoke super.doIt ().

In considering the alternatives, if b.doIt were inaccessible (out of scope)
from A, should the invocation b.doIt follow inheritance such that control
passes directly to a.doIt? Or should the inaccessibility of b.doIt cause
the kind of compile error from the method not existing, even though B
inherits a.doIt?

I think that the case where accessibility is required is where the
superclass defines a kind of abstract factory where the factory
implementations are subclasses, but in separate packages such as would be
provided by independent implementors. If the protected methods were not
accessible the superclass could not invoke its own methods. That is, even
if A didn't know about B, it could still invoke doIt () and it would be
reasonable to expect the subclass to invoke it's implementation of A's
parent method.

Cheers,
Matt Humphrey (e-mail address removed) http://www.iviz.com/
 
J

Justin Fowler

If your calling a method of the super class from a sub class as shown below,
and the method that your trying to call is overloaded, as you have shown
here also, then you need to use the keyword super.

ie.

--- in file A.java ------------
package a;
import b.B;

public class A {
protected void doIt(){
B b = new B();
b.doIt(); // no problem - superclass can access protected methods of
subclass
}
}

--- in file B.java ---
package b;
import a.A;

public class B extends A{
protected void doIt() {
super.doIt(); // This will call Class A. doIt() method.
}
}


Cheers
--

Justin Fowler
Programmer
MizziSoft P/L
Ph: +61 3 9645 7111
Fax: +61 3 9645 7533
http://www.mizzisoft.com

_________________________________________________

This email is subject to professional privilege. If you have received
it in error, any disclosure, copying or use of the contents of this
email is prohibited. If you have received this email in error please
reply to the sender or telephone the sender on +61 3 9645 7111 or fax on
+61 3 9645 7533.
 
A

Andrea Desole

I think that the problem is that, if you call doIt for an instance of A,
you might be actually calling doIt for an instance of another class, not
B, derived from A, for example C. And it makes sense that B doesn't
access a protected member of C.

Andrea
 
M

Matt Humphrey

Jose Rubio said:
Here's a more detailed answer to your question (actually the code is the
best answer!!) In summary, when both the Parent and Child have a method with
the same signature, with both in different packages, you are really
accessing the paren't method. That's why it compiles, but you don't have
access to the child's method.


Hope it helps.

Jose

package FirstGeneration;

public class Parent

{

public void testParent()

{

FirstGeneration.Child homeBoy = new FirstGeneration.Child();


SecondGeneration.Child collegeKid = new SecondGeneration.Child();


//This I can do because I'm accessing my own method,

//not the one overwritten by the subclass,

//so there is no violation, remember a Child is a Parent

//in this case

collegeKid.superClass();

Are you saying that when Parent invokes SecondGeneration child.superClass ()
that it will actually not activate Child.superClass (), but
Parent.superClass () instead? I am using 1.3.1_01 and the protected
subclass method is definately being invoked from the superclass. I have
compiled your code (minus FirstGeneration.Child and Uncle) and invoked
Parent.testParent and it prints "I'm in protected superClass of child. The
code below demonstrates different-package superclass access to protected
subclass.

package a;
import b.B;

public class A {
public A () {
}

protected void doIt () {
System.out.println ("A.doIt");

B b1 = new B ();
b1.doIt ();
}

public static final void main (String arg []) {
(new A ()).doIt ();
}
}
-------------------------
package b;
import a.*;

public class B extends A {

protected void doIt () {
System.out.println ("B.doIt");
}

public void doItA () {
super.doIt ();
}
}

Cheers,
Matt Humphrey (e-mail address removed) http://www.iviz.com/
 
J

John C. Bollinger

Pat said:
I find it surprising that java allows a super class to access the protected
methods of an instance of its subclass, even if the subclass is in a
different package (as illustrated by the code snippet below)
The converse is not true - i.e. a subclass can't access the protected
methods of an instance of the superclass, unless it is in the same package.

To me this seems like a rather arbitrary violation of encapsulation.
I dont think other OO languages I've used (C++ and Delphi), support this
behaviour.

Your example does not compile for me under Java 1.4.2. I get:

====
D:\temp\testdir>javac -classpath . a\A.java
a\A.java:7: doIt() has protected access in b.B
b.doIt(); // no problem - superclass can access protected methods
of subclass
^
1 error
====

The compile-time behavior is described in section 15.12.2 of the JLS:
when the compiler encounters a method invocation expression of the form
obj.method(...), it searches the class corresponding to the type of the
obj reference (including superclasses) for a method that is *both*
"applicable" and "accessible". If there is more than one such then Java
has a detailed procedure with which to determine which method to choose.

The spec is a bit vague about how the search for a suitable method
proceeds. In particular, a naive implementation might assume that all
methods of the applicable class and all superclasses should be examined,
and presumably that is what happens in any compiler that does not
generate an error for your test code. In that case, the compiler would
specify the superclass' method for invocation. VIRTUAL invocation in
this case, which becomes important later. A more discerning reading of
the spec might realize that it must discard all methods that are
overridden by methods in a subclass included in the search; presumably
that is what my compiler is doing.

It is also worthwhile to consider what should happen at run-time,
however, if your classes were compiled without error. This is described
in JLS 15.12.4 and elsewhere. Since you have a virtual method
invocation, the subclass method will be selected by dynamic method
lookup. That method is inaccessible, however, and as a result you
should get a runtime exception (most likely an IllegalAccessError,
actually, though technically JLS only says that "various errors may
occur"). JLS appears to be mistaken when it says, near the end of
section 15.12.4.4, "The above procedure will always find a non-abstract,
accessible method to invoke, provided that all classes and interfaces in
the program have been consistently compiled," or alternatively, that
claim can be taken as proof that the more discerning interpretation of
the method search procedure is intended.

Any way about it, Java is not doing what you think it's doing.


John Bollinger
(e-mail address removed)
 
J

Jose Rubio

Are you saying that when Parent invokes SecondGeneration child.superClass
()
that it will actually not activate Child.superClass (), but
Parent.superClass () instead? I am using 1.3.1_01 and the protected
subclass method is definately being invoked from the superclass. I have
compiled your code (minus FirstGeneration.Child and Uncle) and invoked
Parent.testParent and it prints "I'm in protected superClass of child. The
code below demonstrates different-package superclass access to protected
subclass.
You are correct. I actually never ran the code, but did now and got your
same results. I coded it in Eclipse, and when you mouse over the method it
tells you the fully qualified name of the method you are accessing. In that
scenario Eclipse got confused and was saying that it was the parent's method
and not the subclass's one.

Anyway if you follow the OO guidelines a superclass should never know about
a specific subclass, but it's still intriguing the way Java behaves.

package a;
import b.B;

public class A {
public A () {
}

protected void doIt () {
System.out.println ("A.doIt");

B b1 = new B ();
b1.doIt ();
}

public static final void main (String arg []) {
(new A ()).doIt ();
}
}
-------------------------
package b;
import a.*;

public class B extends A {

protected void doIt () {
System.out.println ("B.doIt");
}

public void doItA () {
super.doIt ();
}
}

Cheers,
Matt Humphrey (e-mail address removed) http://www.iviz.com/
 
W

Woebegone

Anyway if you follow the OO guidelines a superclass should never know about
a specific subclass, but it's still intriguing the way Java behaves.
In fact the superclass doesn't know about the subclass (except by virtue of
referring to an instance). What happens is that all methods in Java are
virtual, so calling an overridden method in a subclass object executes the
method as it is defined on that object. This is true regardless of the
static type of the object. The superclass has acces to its own protected
methods, so for an object 'a' of type 'A' the call a.doIt() must be legal.
If B extends A then B "is an" A, hence internally, for an object 'b' of type
'B' where 'B' extends 'A', a call b.doIt() must be legal also. Since the
call applies to the dynamic type of the object, the mothod defined on B
executes.

Here's another way of looking at it:

public class A {
private A a;
protected void doIt() {
System.out.println("A::method");
}
void method() {
a = new B(); // OK because 'B' is an 'A'
a.doIt(); // executes B.method
}
public static void main(String[] args) {
A a = new A();
a.method();
}
}

class B extends A {
protected void doIt() {
System.out.println("B::method")
}
}

The static type is 'A' so the call to the protected method method doIt must
be visible within an 'A' or any subclass of 'A'. However, in the call
"a.method", polymorphism kicks in and the override from the dynamic object,
which has type 'B' kicks in.

As for visibility, protected members are visible to all subclasses of an
type regardless of package. Package visible members can be seen by all
members of the declaring class's package, regardless of whether they live in
the same inheritance hierarchy.

HTH!

Sean.
 
J

Jose Rubio

Your explanation is totally correct and clearly explain polymorphism. The OP
was talking about this protected access occuring outside the package
boundaries. Since the acces is allowed, I assume that the whole package
visibility goes down the drain when dealing with subclasses/superclasses. I
haven't read the JLS, but it looks like Java puts all those methods in the
same virtual table for that inheritance tree, so maybe there is no way for
it to make the distinction.

Jose

Woebegone said:
Anyway if you follow the OO guidelines a superclass should never know about
a specific subclass, but it's still intriguing the way Java behaves.
In fact the superclass doesn't know about the subclass (except by virtue of
referring to an instance). What happens is that all methods in Java are
virtual, so calling an overridden method in a subclass object executes the
method as it is defined on that object. This is true regardless of the
static type of the object. The superclass has acces to its own protected
methods, so for an object 'a' of type 'A' the call a.doIt() must be legal.
If B extends A then B "is an" A, hence internally, for an object 'b' of type
'B' where 'B' extends 'A', a call b.doIt() must be legal also. Since the
call applies to the dynamic type of the object, the mothod defined on B
executes.

Here's another way of looking at it:

public class A {
private A a;
protected void doIt() {
System.out.println("A::method");
}
void method() {
a = new B(); // OK because 'B' is an 'A'
a.doIt(); // executes B.method
}
public static void main(String[] args) {
A a = new A();
a.method();
}
}

class B extends A {
protected void doIt() {
System.out.println("B::method")
}
}

The static type is 'A' so the call to the protected method method doIt must
be visible within an 'A' or any subclass of 'A'. However, in the call
"a.method", polymorphism kicks in and the override from the dynamic object,
which has type 'B' kicks in.

As for visibility, protected members are visible to all subclasses of an
type regardless of package. Package visible members can be seen by all
members of the declaring class's package, regardless of whether they live in
the same inheritance hierarchy.

HTH!

Sean.
 
W

Woebegone

Jose Rubio said:
Your explanation is totally correct and clearly explain polymorphism. The OP
was talking about this protected access occuring outside the package
boundaries. Since the acces is allowed, I assume that the whole package
visibility goes down the drain when dealing with subclasses/superclasses. I
haven't read the JLS, but it looks like Java puts all those methods in the
same virtual table for that inheritance tree, so maybe there is no way for
it to make the distinction.

Jose

Thanks Jose,

You're right -- I didn't read the OP as closely as I should have. Hope I'm
straightened out now!

Regards,
Sean.
 
W

Woebegone

This is interesting, but I'm not sure which rules in the JLS apply. Section
6.6.2 says that a protected method may be accessed from outside its package
only by the code responsible for implementing that method. In your example,
if class B had a protected method named otherDoIt (), it would not be
accessible in A. Also, access control cannot be more restrictive in
subclasses. That is, it's not possible for b.doIt to be private.

In 6.6.2.1, the JLS goes on (I'm going to try to paraphrase to be sure I'm
understanding this)

- If a class C declares a protected member m, then m can be accessed by
qualified name within a subclass, BUT the type of the qualifier must be the
type of the subclass or one of it's derivatives. This precludes a subclass
calling to arbitrary depth up it's hierarchy.

Now I quote:
<jls>
"6.6.2.1 Access to a protected Member
"Let C be the class in which a protected member m is declared. Access is
permitted only within the body of a subclass S of C. In addition, if Id
denotes an instance field or instance method, then:
" - If the access is by a qualified name Q.Id where Q is an ExpressionName,
then the access is permitted if and only if the type of the expression Q is
S or a subclass of S.
" - If the access is by a field access expression E.Id where E is a Primary
Expression, or by a method invocation E.id(...), where E is a primary
expression, then the access is permitted if and only if the type of E is S
or a subclass of S."
</jls>

I think that in this case the package question is a bit of a red herring,
covered off by 6.6.1.

So back to the original question, I think that the visibility of B.doIt()
from within A is a by-product of polymorphism, and the invisibility of
A.doIt() from within B is encapsulation. Since package visibility allows
easy access across related (trusted) types, that violation of encapsulation
is permitted.

Note that if the method doIt is not defined on A, it is not visible in B
from A. To follow through, I think that the declaration of a method in a
superclass, by virtue of dynamic type resolution, ensures the possibility
that a superclass method will invoke the subclass's method implicitly. What
the above passage does is make it explicit. In that light, it's not really
such an egregious violation of encapsulation.

Regards,
Sean (turning into a windbag!).
 
P

Pat Ryan

Yes I get the same when I compile with the 1.4 option - a compiler error
saying the method is not visible.

Looks like this issue has been fixed in 1.4
 
P

Pat Ryan

just to clarify/address a few points which have come up in response to this
posting

1) The issue seems to be fixed in 1.4. When compiled with the 1.4 source
option, you get an error saying the method is not visible. Sorry I should
have checked this before raising it. But it has generated some interesting
discussion on encapsualtion and polymorphism, so I guess not a waste of
time.

2) as was pointed out by several respondents, the behaviour described only
applies to overridden protected methods. So the issue is more correctly
stated as

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

3) the method invokes is definitely the subclasses implementation, as
demonstrated by the following program
which outputs

A.doIt()
B.doIt()



--- in file A.java ------------

package a;
import b.B;

public class A {
protected void doIt(){
System.out.println("A.doIt()");
B b = new B();
b.doIt(); // no problem - superclass can access protected methods of
subclass
//b.doIt2(); - won't compile doIt2 is not visible
}

public static void main(String[] args){
A a = new A();
a.doIt();
}
}


--- in file B.java ---

package b;
import a.*;

public class B extends A{
protected void doIt() {
System.out.println("B.doIt()");
A a = new A();
//a.doIt(); // won't compile subclass can't access protected methods of
an instance of superclass
}
protected void doIt2(){
System.out.println("B.doIt2()");
}
}


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

Woebegone

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.
Hi Pat,

I think you've raised a very interesting question, and I have to admit I was
surprised to learn of the package visibility of protected methods. In C++
terms, it's like package colleagues are friends -- I though encapsulation
would dominate.

But, consider the case where a superclass declares an abstract method, then
calls it as in a factory method as mentioned earlier. Any concrete subclass
must provide an implementation of the method, so the superclass knows a
priori it's there at runtime (this is a runtime rule inferable at compile
time). The code that executes will be that of the runtime object's class:
this is why I said earlier that the superclass implicitly can access the
sublass's implementation.

Then, we know in Java that it's impossible to call up the hierarchy to
arbitrary depth: it's "this," or "super." As an earlier poster mentioned, no
class can infer at which level a member is implemented.

So, I think that the compiler has to behave the way it does to accommodate
polymorphism, while doing what it can to enforce encapsulation. Polymorphism
is a runtime thing, but it requires compile time support.

Protected access supports polymorphic hierarchies; package access is, as I
see it anyway, a pragmatic violation of encapsulation.

Regards,
Sean.
 
C

Chris Uppal

Woebegone said:
this is why I said earlier that the superclass implicitly
can access the sublass's implementation.

The superclass can IMPLICITLY access the subclass's implementation, yes, but it
cannot EXPLICITLY do so. Being the superclass does not give it any special
rights.

So it can invoke a method on an object referred to by a variable of the
superclass type (that dynamically resolves to the overridden method in the
subclass), but it cannot *directly* invoke the method of the subclass (even if
it would resolve to the same method of the same object).

Access control is a static concept, and it applies to the declared types of the
variables involved, not to the actual types of the objects those variables
refer to at runtime[*].

So, I think that the compiler has to behave the way it does to accommodate
polymorphism, while doing what it can to enforce encapsulation.

The 1.3 range compilers were allowing it, but that was a bug, nothing more.

-- chris

* Actually there is run-time access control too, as is described in the JVM
spec, but that's irrelevant here because:

A) The compiler is required to emit code such that the JVM's runtime checks
should coincide with its compile-time checks (I suspect that this is
fundamentally what the 1.3 compilers are getting wrong).

B) As far as I can tell the 1.4 JVMs don't actually implement those checks.
(Which I find so surprising that I still don't entirely trust my results --
I'll do a bit more checking later).
 
W

Woebegone

The superclass can IMPLICITLY access the subclass's implementation, yes, but it
cannot EXPLICITLY do so. Being the superclass does not give it any special
rights.

So it can invoke a method on an object referred to by a variable of the
superclass type (that dynamically resolves to the overridden method in the
subclass), but it cannot *directly* invoke the method of the subclass (even if
it would resolve to the same method of the same object).

According to 6.6.2.1 in the JLS, it looks as though a superclass can
directly refer to a subclass method through a reference to an object of
subclass type, as long as the method is accessible in the superclass.
Access control is a static concept, and it applies to the declared types of the
variables involved, not to the actual types of the objects those variables
refer to at runtime[*].

Agreed, but what I'm saying is that in the case where a superclass might
implicitly gain access dynamically to a given implementation, the compiler
will statically allow direct reference. That said, I'm not claiming that
it's possible to specify arbitrarily what code actually executes -- just
that the direct member reference is legal from a superclass.
The 1.3 range compilers were allowing it, but that was a bug, nothing more.

-- chris

Going back to the original example, I found the same results as the OP using
1.3.1 and 1.4.2, and still think the observed behaviour is correct based on
my reading of the JLS.

Regards,
Sean.
 
J

Jose Rubio

Thank god they fix all this in 1.4. I was starting to go nuts!!! I couldn't
really understand why a superclass could have special privileges on
subclasses of other packages.

Now I understand why Eclipse was displaying that I was trying to access the
parent method instead of the subclass one. When I switched the compiler
compliance level to 1.4 the error just popped right up as expected. I'm
relieved!!

Jose

Woebegone said:
The superclass can IMPLICITLY access the subclass's implementation, yes, but it
cannot EXPLICITLY do so. Being the superclass does not give it any special
rights.

So it can invoke a method on an object referred to by a variable of the
superclass type (that dynamically resolves to the overridden method in the
subclass), but it cannot *directly* invoke the method of the subclass (even if
it would resolve to the same method of the same object).

According to 6.6.2.1 in the JLS, it looks as though a superclass can
directly refer to a subclass method through a reference to an object of
subclass type, as long as the method is accessible in the superclass.
Access control is a static concept, and it applies to the declared types of the
variables involved, not to the actual types of the objects those variables
refer to at runtime[*].

Agreed, but what I'm saying is that in the case where a superclass might
implicitly gain access dynamically to a given implementation, the compiler
will statically allow direct reference. That said, I'm not claiming that
it's possible to specify arbitrarily what code actually executes -- just
that the direct member reference is legal from a superclass.
The 1.3 range compilers were allowing it, but that was a bug, nothing more.

-- chris

Going back to the original example, I found the same results as the OP using
1.3.1 and 1.4.2, and still think the observed behaviour is correct based on
my reading of the JLS.

Regards,
Sean.
 
C

Chris Uppal

Woebegone said:
According to 6.6.2.1 in the JLS, it looks as though a superclass can
directly refer to a subclass method through a reference to an object of
subclass type, as long as the method is accessible in the superclass.

I'm not sure if we are misunderstanding each other here, or just disagreeing on
a question of fact. Anyway on the assumption that there is still some point to
this discussion...

This is the exact case I am considering:

====== A.java =======
package a;
public class A
{
protected void method() { System.out.println("in A.method()"); }
public static void main(String[] args)
{
b.B theB = new b.B();
A aliasForTheB = theB;
aliasForTheB.method(); // allowed, calls b.B.method()
theB.method(); // illegal: access not permitted
}
}
====== B.java =======
package b;
public class B extends a.A
{
protected void method() { System.out.println("in B.method()"); }
}
=============

This example fails to compile using Sun's JDK 1.4.2-b28, although it does
compile under JDK 1.3.

I believe that the 1.4 behaviour is correct since the relevant section states:

Let C be the class in which a protected member m is declared.
Access is permitted only within the body of a subclass S of C.

Now b.B.method() is declared in b.B, but the disallowed call is being made from
a.A, which is *not* a subclass of b.B (or in the same package), hence it is
illegal.

The spec then goes on to say:

In addition, if Id denotes an instance field or instance method, then:
• If the access is by a qualified name Q.Id, where Q is an ExpressionName,
then the access is permitted if and only if the type of the expression Q is
S or a
subclass of S.
• If the access is by a field access expression E.Id, where E is a Primary
expression, or by a method invocation expression E.Id(. . .), where E is a
Primary expression, then the access is permitted if and only if the type of
E is
S or a subclass of S.

which I suspect is what you are focussing on. However -- IMO -- these extra
conditions are irrelevant since the initial condition has failed.

It is important to realise (as the 1.3 compiler writers apparently did not ;-)
that the declaration of a.A.method() is totally irrelevant here. The compiler
is required to consider the call to b.B.method() exactly as if it were not
overriding anything. The reason for this is in the (very important, but often
overlooked) Binary Compatibility section of the spec. Code of the following
form (untested):

====== A.java =======
package a;
public class A
{
public void method() { System.out.println("in A.method()"); }
}
====== B.java =======
package b;
public class B extends a.A
{
public void method() { System.out.println("in B.method()"); }
}
====== C.java =======
package c;
public class C
{
public static void main(String[] args)
{
b.B theB = new b.B();
theB.method();
}
}
=============

this code must continue to work if classes c.C and b.B are loaded into the JVM
with a newer version of class a.A that does not include method().

That is, the compiler *must not* say "ah, the call to theB.method() is an
override of the a.A.method, so I will generate code with an embedded reference
to a.A.method() that will be resolved to b.B.method() by the runtime
machinery".

The 1.3 compiler gets this wrong and does not ignore a.A.method() as it should.
I managed to find a 1.3 compiler and this is the disassembled output of
a.A.main() from the first example in this post (this is disassembled with
gnoloo, since javap can't be persuaded to show enough detail):

=============
..method public static main ([Ljava/lang/String;)V
new b/B
dup
invokespecial b/B/<init> ()V
astore_1
aload_1
astore_2
aload_2
invokevirtual a/A/method ()V
aload_1
invokevirtual a/A/method ()V
return
=============

The second last line is wrong -- javac is generating a call to a.A.method()
when it should be generating a call to b.B.method().

Access control is a static concept, and it applies to the declared
types of the variables involved, not to the actual types of the objects
those variables refer to at runtime[*].

Agreed, but what I'm saying is that in the case where a superclass might
implicitly gain access dynamically to a given implementation, the compiler
will statically allow direct reference.

No it doesn't, and no it shouldn't ;-)

1.3 is broken in this respect, but that is Just Another Bug(tm).

-- chris
 

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
473,755
Messages
2,569,536
Members
45,015
Latest member
AmbrosePal

Latest Threads

Top