How are java.lang.Object's protected methods protected from subclasses?
Solution 1
You can only access protected members of a type in a different package if the compile-time type of the expression you're referencing it through is either your own class or a subclass. (Where "your" class is the class containing the code.) Your own class has to be a subclass of the type which originally declares the method, too.
Here's an example; assume that Base
is in a different package to all the other classes:
package first;
public class Base
{
protected void Foo() {}
}
// Yes, each class is really in its own file normally - but treat
// all the classes below as being in package "second"
package second;
public class Child extends Base
{
public void OtherMethod(Object x)
{
((Base) x).Foo(); // Invalid: Base is not Child or subclass
((Child) x).Foo(); // Valid: Child is Child
((GrandChild) x).Foo(); // Valid: GrandChild is subclass of Child
((OtherChild) x).Foo(); // Invalid: OtherChild is not Child or subclass
}
}
public class GrandChild extends Child {}
public class OtherChild extends Base {}
In other words, it's letting you have access to the protected members of "objects which are a like you".
Details are in section 6.6.2 of the Java Language Specification:
A
protected
member or constructor of an object may be accessed from outside the package in which it is declared only by code that is responsible for the implementation of that object.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 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.
Solution 2
When you said "((Object) this).clone()
", you accessed your own object via its superclass Object. You performed a widening conversion to an Object. The code then attempts to call clone on Object.
But, as you've noted, clone is a protected method, meaning that only if your object was in the same package of java.lang would it be able to access the OBJECT's clone method.
When you say this.clone, your class extended Object and thus had access to override or use clone directly through the protected class modifier because of inheritance. But that doesn't change the Object implementation.
By saying ((Object) yourObject), you get something that is only accessible through the Object class. Only public methods of the Object class are accessible ouside of the package java.lang, so you get the compile-time exception, because the compiler knows this.
By saying this.clone(), you are invoking your object's clone method that it got through inheriting through Object, and is now able to be invoked because it becomes a part of your custom subclass.
Solution 3
The difference is how you are accessing Object.clone(). clone is only accessible when accessed via a sub class or class in the same package. In the getOne() example you are claling this.clone(). This clearly satisfies access from within a sub class.
In getTwo() though you are acessing Object.clone() and not TestClass.clone(). In order for this to work you must have package level access to Object which you don't and hence the error.
Adrian Heine
Software developer with a focus on web development. Currently into JavaScript, TypeScript, Rust and Node.js, extensive experience with PHP.
Updated on June 03, 2022Comments
-
Adrian Heine about 2 years
The keyword
protected
grants access to classes in the same package and subclasses (http://java.sun.com/docs/books/tutorial/java/javaOO/accesscontrol.html).Now, every class has
java.lang.Object
as superclass (http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Object.html).Hence I conclude that every class may access
java.lang.Object
's methods even if they areprotected
.Take a look at the following example:
public class Testclass { public Object getOne() throws CloneNotSupportedException { return this.clone(); } public Object getTwo() throws CloneNotSupportedException { return ((Object) this).clone(); } }
While
getOne()
compiles fine,getTwo()
givesTestclass.java:6: clone() has protected access in java.lang.Object return ((Object) this).clone();
I neither understand why
getTwo()
doesn't compile nor what's the difference (regarding the access ofjava.lang.Object
s members) withgetOne()
. -
palantus over 15 yearsYou're confusing protected with package-protected.
-
palantus over 15 yearsNote from that reference that classes can access protected members of any class in the same package.
-
Adrian Heine over 15 yearsYou have a typo: <pre> public class Child extends Base {} { […] } </pre>
-
palantus over 15 yearsI was nitpicking with this statement: "But, as you've noted, clone is a protected method, meaning that only if your object was in the same package of java.lang would it be able to access the OBJECT's clone method." On the face of it, that sounds wrong. But let me reconsider for a bit...
-
Adrian Heine over 15 yearsHm. While your copy from Javaspec sounds persuading, your code compiles very well. Still seems to be different from my example.
-
palantus over 15 yearsOk, I think I understand it now. +1 for making me think.
-
Jon Skeet over 15 yearsIt's due to the package business - I amended the text to say that Base had to be in a different package. I'll put it in the code as well.
-
Adrian Heine over 15 yearsAh sure. Else the package-way of accessing the protected method would still work.
-
MetroidFan2002 over 15 yearsSorry, mmyers. What I was saying was that your class can't access Object's clone method, but it can use or override it by being a subclass of object: it could only access the Object implementation (read: use Object.clone(), not yourclass.clone()) by being in the same package, namely, java.lang.
-
fasih.rana about 13 years((OtherChild) x).Foo(); Shouldn't this be Invalid?
-
Jon Skeet about 13 years@fasih: Yes, whoops. The explanation was right, but with the wrong result :)
-
ajmartin about 12 yearsJust need a bit more clarity on this
((OtherChild) x).Foo();
is invalid because the call toFoo()
is outside the classOtherChild
and not because of the comment mentioned there which is - Invalid: OtherChild is not Child or subclass. I can callFoo();
from withinclass OtherChild()
. -
Jon Skeet about 12 years@ajmartin: No, it is because of exactly that comment - within Child, you can only call
foo()
on aChild
instance or a subclass.