I've got some doubts regarding protected identifier. In the first chapter of Sun Certified Java Programmer Study Guide by K.Sierra I found the following information:
"Once the subclass-outside-the-package inherits the protected member, that member (as inherited by the subclass) becomes private to any code outside the subclass, with the exception of subclasses of the subclass."
I provided sample code which reflects the above statement and it is absolutely clear to me.
// Parent class
package package1;
import package2.Child;
public class Parent {
protected int i = 5;
}
// Child class
package package2;
import package1.Parent;
public class Child extends Parent {
// variable 'i' inherited
}
package package2;
public class Neighbour {
public void protectedTesting(){
Child child = new Child();
System.out.println(child.i); // no access
}
}
I've started experimenting and made a small change - moved Neighbour to package1. And there is an access to "i" variable which is a little bit surprising for me as it is not in accordance to statement "becomes private to any code outside the subclass"
Neighbour class after change:
package package1;
import package2.Child;
public class Neighbour {
public void protectedTesting(){
Child child = new Child();
System.out.println(child.i); // access!
}
}
Please clarify it to me. Thanks.
In short, protected is package-private as well as visible to subclasses. Even the JLS is vague on this (JLS ยง6.6.2):
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.
It specifies that outside the package, only subclasses can access protected members. This implies that you can also access the variable within the package. It's poor wording, but true nonetheless that protected members have package-level visibility as well as subclass-level visibility.
See also:
This related question
The Java Trail for access control
And there is an access to "i" variable which is a little bit surprising for me as it is not in accordance to statement "becomes private to any code outside the subclass"
--> But you moved class Neighbour in package package1 which is true according to "Protected members can be accessed by classes in same package"
"Once the subclass-outside-the-package inherits the protected member, that member (as inherited by the subclass) becomes private to any code outside the subclass, with the exception of subclasses of the subclass."
--> Inside package it is still protected and not private for all classes within the package.
The truth is not in "Sun Certified Java Programmer Study Guide" but in the Java Language Specification
6.6.2. Details on protected Access
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.
protected visibility includes package level visibility.
Inheritance allows you to treat your Child object as an instance of Parent.
As the member i of Parent is declared in the same package, it is accessible from Neighbour.
package package1;
import package2.Child;
public class Neighbour {
public void protectedTesting() {
Parent neighboured = new Child();
System.out.println(neighboured.i); // access
}
}
Related
Recently I've faced a problem getting a runtime error java.lang.IllegalAccessError when trying to access from inner class a protected field declared in outer's parent class that was loaded by a different class loader. Briefly:
Class Parent has protected field p.
Class Outer extends Parent.
Class Inner is an inner class defined in class Outer.
Inside Inner class there's a code: Outer.this.p.
All classes are declared in the same package.
Normally it's compiled and runs fine until Parent and Outer class are loaded by different class loaders. In this case we get java.lang.IllegalAccessError when trying to access Outer.this.p from Inner.
I found an old bug report (which appeared to be a feature) describing this behavior:
https://bugs.java.com/bugdatabase/view_bug.do?bug_id=6258289
But resolution sounds contradictive to me:
The key is that in the failing case the inner class isn't in the same
package (and isn't a subclass of) ConcreteCommand/AbstractCommand.
This is simply a violation of the Java specification for protected
classes.
It sounds correct. But if we declare Parent and Outer classes in different packages but load with the single class loader (simply create sample console app without any jar loadings) we don't get any errors. So technically it's a violation of Java spec for protected classes but since we use an inner class it works.
So we have different behavior for two cases of "different packages".
Declared in different packages, loaded by single class loader - OK.
Declared in single package, loaded by different class loaders - NOT OK.
Could someone give a clear explanation of how inner class gets access to parent's fields and why it works differently for two cases?
Same class loader it seems to be working
Did I get the question right?
Do you have any unit test case to reproduce your issue?
Parent Class
package p1;
public class Parent {
protected String p = "Value from Parent";
public void test() {
System.out.println(p);
}
}
Outer Class
package p1;
public class Outer extends Parent {
class Inner {
public void test() {
Outer.this.p = "Value set from Inner";
System.out.println(Outer.this.p);
}
}
public void test() {
new Inner().test();
}
}
Main Class
package p1;
public class Main {
public static void main(String[] args) {
Parent p = new Parent();
p.test();
p = new Outer();
p.test();
}
}
Output
Value from Parent
Value set from Inner
Declared in different packages, loaded by single class loader - OK
'protected' access is considerate about parent-child relation among classes and allows child classes to access 'protected' members of parent even if they are in different packages. So, I think this is as expected.
Declared in single package, loaded by different class loaders - NOT OK
This has to do with runtime packages. Check this.
Now we know that Parent is in different runtime package than Outer and Inner due to being loaded via two different class loaders. At the same time, we also have to remember that Outer is 'child' of Parent but Inner is not. Inner doesn't have an 'Is-a' relation with Parent.
Putting it all together :
Since Parent is in a different runtime package, Inner is not able to access Parent's 'protected' members as Inner is not a child of Parent.
Look, PlanetShape extends Shape!
And yet it says isOverlapMethodLevel have protected access in Shape!
Did I just discovered a bug in android studio? Or is it something else?
The packages differ in your class hierarachy - specifically your PlanetShape class is in a different package to the classes it extends.
Directly from Java documentation: (emphasis mine)
The protected modifier specifies that the member can only be accessed
within its own package (as with package-private) and, in addition, by
a subclass of its class in another package.
Clearly the "by a subclass of its class in another package" seems to be the cause of some confusion and I agree that the wording is rather ambiguous.
Basically, what they're trying to say is you can do this:
public class PlanetShape extends Shape {
aMethod() {
// call the protected method declared in the Super class (ok)
doProtectedMethodOfShape();
}
}
But not this:
public class PlanetShape extends Shape {
aMethod() {
Shape s = new Shape();
s.doProtectedMethodOfShape(); // error here
}
}
In the second example, you get an error because you're not accessing a protected method via inheritance, you're just trying to access a protected member function from an instance in another package.
Java experts, I would sincerely appreciate any insights!
I have an abstract class in a package with a protected method. I also have a subclass of this class in the same package. Now, when I try to instantiate the subclass from a class outside the package, and invoke the protected method on the subclass' instance, Eclipse is complaining the protected method is not visible.
I thought, protected methods will be visible to all children - in or out of the package - as long as the class visibility does not restrict it - in this case, both the parent and the child class are public. What am I missing? Thanks in advance!
package X;
public abstract class Transformation {
protected OutputSet genOutputSet (List list) {
..
}
}
package X;
public class LookupTransformation extends Transformation {
}
package Y;
import X.*;
public class Test {
public static void main(String[] args) {
List<field> fld_list = new ArrayList();
..
LookupTransformation lkpCDC = new LookupTransformation();
OutputSet o = lkpCDC.genOutputSet(fld_list); // Eclipse errors out here saying genOutputSet from the Type Transformation is not visible. WWWWWWWWHHHHHAAAATTTTTT????
}
}
protected access means genOutputSet can be called by classes inheriting from the class where it's declared or by classes belonging to the same package. This means you can call it from within LookupTransformation.
However, you are trying to call it from an unrelated class - Test - located in a different package, which requires public access.
See additional explanation here.
Your code is not in a subclass (you're in Test), and your code is not in the
same package (you're in Y). So the method is not visible. That's normal.
protected means you may call the method in any derived class. However, Test isn't derived from Transformation. genOutputSet is only visible inside Transformation and LookupTransformation. This doesn't tell anything about the visibility of methods when they are called on an object of the derived class.
The best possible answer I could give would be in the form of this picture that I used to learn it myself:
Protected methods work on subclasses(inherited classes in your case) that are in other packages aswell. You are however calling it from a different class(not subclass). Hope this helps!
Iam preparing for SCJP , also i came to know that protected members scope is within the package as well as in other package with some conditions like possible only with inheritance.
For example :
i have three classes as Parentclass Childclass Friendclass
package x.parent;
class Parentclass{
protected int x=10;
...............
}
package x.child;
class Childlass extends Parentclass{
super.x=20;
...............
}
package x.child;
import x.parent.Parentclass;
class Friendclass{
Parentclass pc = new Parentclass();
pc.x=30;
...............
}
Whats the reason behind that, in Friendclass the member x will not accept to assign a value to that, behaves as private member not in case of Childclass.
There are four access modifiers
private - just this class
no modifier - just this class or this package (NOT subclass)
protected - just this class, this package, or subclass
public - everyone and their cousin
Since it uses the default modifier, it has access if one of the following is true:
Is part of the class itself (Nope!)
Is part of the package of the class itself (Nope!)
So it fails the criteria, and so you don't get access.
You can't even access Parentclass.x in Childclass because x has default visibility (not protected). See http://download.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
edit:
x.child.Friendclass is not in the same package as x.parent.Parentclass.
x.child.Friendclass does not inherit from x.parent.Parentclass.
as TotalFrickinRockstarFromMars's summary states and the Java access control docs also state, this means that Friendclass is not allowed to access the field x.
In java, there's three levels of access:
Public - Open to the world
Private - Open only to the class
Protected - Open only to the class and its subclasses (inheritance).
So why does the java compiler allow this to happen?
TestBlah.java:
public class TestBlah {
public static void main(String[] args) {
Blah a = new Blah("Blah");
Bloo b = new Bloo("Bloo");
System.out.println(a.getMessage());
System.out.println(b.getMessage()); //Works
System.out.println(a.testing);
System.out.println(b.testing); //Works
}
}
Blah.java:
public class Blah {
protected String message;
public Blah(String msg) {
this.message = msg;
}
protected String getMessage(){
return(this.message);
}
}
Bloo.java:
public class Bloo extends Blah {
public Bloo(String testing) {
super(testing);
}
}
Actually it should be:
Open only to the classes on the same package the class and its subclasses (inheritance)
That's why
Because protected means subclass or other classes in the same package.
And there's actually a fourth "default" level of access, when the modifier is omitted, which provides access to other classes in the same package.
So protected is between default and public access.
To be more specific, you're expecting protected to work as it does in C++.
However, in Java, it has a different meaning. In Java, a protected method is available to the class (obviously), all the other classes in the same package and any subclasses of this class. Classes in other packages will not have access unless they subclass this original class.
See this similar question for more specific information on inheritance markers.
Personally, I almost never use protected. I develop applications rather than frameworks so I'm much more likely to define public methods, private data and, quite often, mark my whole class as final.
There are actually four levels of access: "public", "protected", "private" & default also known as package private or package protected. Default limits accessibility to the package. Default is quite useful and I use it frequently.
You're able to call b.getMessage() because b is of type Bloo, which extends Blah, and getMessage() is protected. Protected, as you mentioned, allows subclasses to access the method.
You've got the following errors, though:
Calling super() with no arguments in the Bloo constructor is an error. The compiler can't find the no-parameter Blah constructor because you defined one with a String parameter.
Calling new Blah() in TestBlah main method is an error for the same reason as above.
Referring to a.testing and b.testing is an error because you didn't define the variable testing for any class.