Java: protected access across packages - java

I would like to understand what's happening in the example below (where a protected member is being accessed from outside the package through a subclass).
I know for classes outside the package, the subclass can see the protected member only through inheritance.
There are two packages: package1 and package2.
package1: ProtectedClass.java
package org.test.package1;
public class ProtectedClass {
protected void foo () {
System.out.println("foo");
}
}
package2: ExtendsprotectedClass.java
package org.test.package2;
import org.test.package1.ProtectedClass;
public class ExtendsprotectedClass extends ProtectedClass {
public void boo() {
foo(); // This works,
// since protected method is visible through inheritance
}
public static void main(String[] args) {
ExtendsprotectedClass epc = new ExtendsprotectedClass();
epc.foo(); // Why is this working?
// Since it is accessed through a reference,
// foo() should not be visible, right?
}
}
package2: UsesExtendedClass.java
package org.test.package2;
public class UsesExtendedClass {
public static void main(String[] args) {
ExtendsprotectedClass epc = new ExtendsprotectedClass();
epc.foo(); // CompilationError:
// The method foo() from the type ProtectedClass
// is not visible
}
}
It is understood that the boo() method in ExtendsprotectedClass can access foo(), since protected members can be accessed through inheritance only.
My question is, why is the foo() method working fine when accessed through a reference in the main() method of ExtendsprotectedClass but will not work when accessed through the epc reference in UsesExtendedClass?

Code within the ExtendsprotectedClass class is allowed to access protected members of ProtectedClass via a reference of type ExtendsprotectedClass. From the JLS section 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.
and
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. [...]
UsesExtendedClass isn't reponsible for the implementation of ExtendsprotectedClass, hence the final call fails.
EDIT: The reasoning behind this is that protected access is designed to help subclasses implement the functionality they need, giving more access to the internals of the superclass than would normally be available. If that were available to all code, it would be pretty close to making the method public. Basically, the subclasses are trusted not to break encapsulation; they're given more capabilities within objects of their own type. The public API shouldn't expose those details, but the protected API can just for the purposes of giving subclasses more opportunities.

It is working in the first case because it is being called from the same class even the method is being accessed through a reference. You could even call a private method of ExtendsprotectedClass through a reference in the same main method.

I believe you've answered your own question; UsesExtendedClass does not inherit from ProtectedClass, and -- by definition -- "protected" members are accessible only in the class in which they are declared / defined or in a class that inherits from the one in which they are declared or defined.

take a look on this picture from : http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
its clear that protected member of class can be accessed via subclass.

Related

Java, protected access of methods of nested subclass not working. Forced to public [duplicate]

I would like to understand what's happening in the example below (where a protected member is being accessed from outside the package through a subclass).
I know for classes outside the package, the subclass can see the protected member only through inheritance.
There are two packages: package1 and package2.
package1: ProtectedClass.java
package org.test.package1;
public class ProtectedClass {
protected void foo () {
System.out.println("foo");
}
}
package2: ExtendsprotectedClass.java
package org.test.package2;
import org.test.package1.ProtectedClass;
public class ExtendsprotectedClass extends ProtectedClass {
public void boo() {
foo(); // This works,
// since protected method is visible through inheritance
}
public static void main(String[] args) {
ExtendsprotectedClass epc = new ExtendsprotectedClass();
epc.foo(); // Why is this working?
// Since it is accessed through a reference,
// foo() should not be visible, right?
}
}
package2: UsesExtendedClass.java
package org.test.package2;
public class UsesExtendedClass {
public static void main(String[] args) {
ExtendsprotectedClass epc = new ExtendsprotectedClass();
epc.foo(); // CompilationError:
// The method foo() from the type ProtectedClass
// is not visible
}
}
It is understood that the boo() method in ExtendsprotectedClass can access foo(), since protected members can be accessed through inheritance only.
My question is, why is the foo() method working fine when accessed through a reference in the main() method of ExtendsprotectedClass but will not work when accessed through the epc reference in UsesExtendedClass?
Code within the ExtendsprotectedClass class is allowed to access protected members of ProtectedClass via a reference of type ExtendsprotectedClass. From the JLS section 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.
and
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. [...]
UsesExtendedClass isn't reponsible for the implementation of ExtendsprotectedClass, hence the final call fails.
EDIT: The reasoning behind this is that protected access is designed to help subclasses implement the functionality they need, giving more access to the internals of the superclass than would normally be available. If that were available to all code, it would be pretty close to making the method public. Basically, the subclasses are trusted not to break encapsulation; they're given more capabilities within objects of their own type. The public API shouldn't expose those details, but the protected API can just for the purposes of giving subclasses more opportunities.
It is working in the first case because it is being called from the same class even the method is being accessed through a reference. You could even call a private method of ExtendsprotectedClass through a reference in the same main method.
I believe you've answered your own question; UsesExtendedClass does not inherit from ProtectedClass, and -- by definition -- "protected" members are accessible only in the class in which they are declared / defined or in a class that inherits from the one in which they are declared or defined.
take a look on this picture from : http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html
its clear that protected member of class can be accessed via subclass.

How do I make a member visible to its subclasses (which might not be in the same package)?

I have an abstract base class with certain fields/abstract methods in it.
How do I make these visible to its children, even if the children don't live in the same package? (And I think public is a bit too open, where protected would not work!)
(To the comments below, protected DOES NOT work because protected fields are ONLY VISIBLE WITHIN the same package)
To prove the point, the following piece of code doesn't work. Please tell me why not.
package a;
public class Base
{
protected void foo() {}
}
// in a separate file/package
package b;
public class Child extends Base
{
private Base wrappedBase = new Base();
#Override
protected void foo()
{
wrappedBase.foo(); // <<<<< This throws "The method foo from type Base is not visible"
}
}
Normaly, you would just use protected, which does work within subclasses within the restrictions in the JLS:
In foo/Parent.java:
package foo;
public class Parent {
protected int x;
}
In bar/Child.java:
package bar;
import foo.Parent;
public class Child extends Parent {
public Child() {
// Look ma, access to a protected variable declared
// by my superclass in a different package!
x = 10;
}
}
Now the example you've given is slightly different, because you're trying to access the protected variable via a compile-time type of Base... and that doesn't work. You can only access a protected member via an expression with the same compile-time type as the code doing the accessing (or a subtype). As per section 6.2.2.1 of the JLS:
Let C be the class in which a protected member 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.
There is no way of providing access to the protected member of all objects of the superclass to all subclasses. This is deliberate - it prevents code from creating a subclass simply in order to get access to more members of other types.

Java use protected method of superclass in copy constructor of subclass

I have a protected method (I don't want this method to be seen outside Superclass or it subclasses). I want to use it in copy constructor, but I can't. Code:
public class Superclass {
protected HashMap<Object, Object> getData() {
return new HashMap<>();
}
}
public class Subclass extends Superclass {
public Subclass(Superclass abstractClass) {
init(abstractClass);
}
private void init(Superclass abstractClass) {
//ERROR!!! getData has protected access in Superclass
for (Map.Entry<Object, Object> entry : abstractClass.getData().entrySet()) {
//do something
}
}
}
How can I overcome this problem if I still want to create Subclass object from Superclass object without exposing getData() method?
EDIT Classes are in different packages!
You tried to access a method, before creating an instance. The instance is not yet fully initialized.
Calling methods of objects that are being constructed from their own constructor can result in unexpected behavior as the objects are not initialized consistently until the constructor finishes.
While in the same package the following works fine for me:
Superclass:
public class Superclass {
protected List<String> getData() {
return Arrays.asList("a", "b", "c");
}
}
Subclass:
public class Subclass extends Superclass {
public static void main(String[] args) {
Superclass superObject = new Superclass();
Subclass subObject = new Subclass(superObject);
}
public Subclass(Superclass abstractClass) {
//ERROR!!! getData has protected access in Superclass
for (String entry : abstractClass.getData()) {
System.out.println(entry);
}
}
}
Mind you, I'm not sure that's what you're actually trying to do. Why are you passing an instance of Superclass in the constructor of Subclass for example? Also, you had a HashMap rather than a List, but HashMaps aren't Iterable, so you can't use the extended for loop on them.
However:
If those classes are in different packages, things change. Look at the language specifications; in §6.6.1 it says:
[I]f the member or constructor is declared protected, then access is permitted only when one of the following is true:
Access to the member or constructor occurs from within the package containing the class in which the protected member or constructor is declared.
Access is correct as described in §6.6.2.
And §6.6.2 states:
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.
Now, as calling a function in an init method is not considered responsible for implementation, you may not access the member here. Sorry, that's how the language works.
Accessing a protected method from a class in a different package is not possible - see http://docs.oracle.com/javase/tutorial/java/javaOO/accesscontrol.html. The fact that you are inheriting from Superclass does not count, since you are trying to call the method on a reference to Superclass, not on the base class itself.
So, what you are trying to do does not work - if it would, it would break Java's access control: you could then simply inherit from any class and implement a method which takes a super class reference as parameter, and then access all protected methods of the super class (you can, of course, still inherit from the base class and access the protected members of the superclass, even cross-package).
If getData() is a method which needs to be called from another package, you need to make it public.
On a side note, you should probably rethink your design - is it really necessary to pass a reference to a Superclass object in the Subclass constructor?

Protected access in java not working

Consider class test
package access;
public class test {
public String s;
protected test(String s){
this.s = s;
System.out.println("access.test constructor");
}
protected void test1(String s){
this.s = s;
System.out.println("access.test test1 method");
}
}
Consider class Operations
package data;
public class Operations extends access.test{
Operations(String s){
super(s);
}
public static void main(String args []) {
// TODO Auto-generated method stub
//Operations O = new Operations("Operations!");
access.test t = new access.test("hello");//1
t.test1("hi!"); //2
}
}
Constructor test and method test1 is not visible at lines 1 and 2. Why??
In your data.Operations.main() you are attempting to instantiate access.test via new:
access.test t = new access.test("hello");//1
You can't do that. That's what it's telling you via the error.
Section 6.6.1 of the JLS tells us:
A member (class, interface, field, or method) of a reference (class,
interface, or array) type or a constructor of a class type is
accessible only if the type is accessible and the member or
constructor is declared to permit access:
If the member or constructor is declared public, then access is
permitted. All members of interfaces are implicitly public.
Otherwise, if the member or constructor is declared protected, then
access is permitted only when one of the following is true:
Access to the member or constructor occurs from within the package
containing the class in which the protected member or constructor is
declared.
Access is correct as described in §6.6.2.
We jump to 6.6.2.2 and find:
A protected constructor can be accessed by a class instance creation expression (that does not declare an anonymous class) only from within the package in which it is defined.
access.test is in a different package and you declared the constructor protected. Only classes within access can call the constructor directly (e.g. using new - this is what "class instance creation expression" means).
Your data.Operations class extends access.test, which is fine since access.test was declared public. Your constructor is package-private, therefore you are allowed to call:
Operations o = new Operations("Operations!");
in data.Operations.main(). Operation's constructor calls super(s) which it is allowed to do because it's a subclass (in fact, it has to since there's no nullary constructor in the superclass). Note that this is not the same thing as calling the constructor directly via new.
If you were to have this:
Operations(String s){
super(s);
access.test t = new access.test(s);
}
It would produce the same error when trying to use new; you can't do that.
A protected method has different access rules than a protected constructor.
You've declared test1() in access.test as protected.
Declaring a method protected means classes in the access package and subclasses (regardless of package) can call it. Therefore, the following is perfectly valid in data.Operations.main():
Operations o = new Operations("Operations!");
o.test1("hi!");
If your main() was in a different class in your data package (or in another package and Operations had a public constructor), you couldn't do that.
package data;
public class ThirdClass {
public static void main(String[] args) {
// This is perfectly fine since Operations has a package-private constructor
Operations o = new Operations("Some String");
// This won't compile
o.test1("hi!");
}
}
Constructors are not inherited.
use it
public test(String s){
this.s = s;
System.out.println("access.test constructor");
}
A subclass only access a protected members of its parent class, if it involves implementation of its parent. Therefore , you can not instantiate a parent object in a child class, if parent constructor is protected and it is in different package.

What is the default access level for methods in a public abstract class in Java?

Normally the default access level of methods is package local. But it seems to me that it is different for public abstract classes. In those classes the default seems to be public. Is this correct?
Update
#EJP
It was a bug in my code. It is possible to shadow the package local method with a public method, which confuses me. This makes me think that a public abstract could be similar to an interface where the methods are public. See the example:
a/A.java:
package a;
public abstract class A
{
String a () { return "a"; }
}
test_a.java:
class test_a
{
static class NewA extends a.A
{
public String a () { return "new a"; }
}
public static void main (String[] args)
{
NewA a = new NewA();
System.out.println(a.a());
}
}
False, let's see with a quick example:
package apackage;
public abstract class AbstractFoo {
//A method with default visibility
abstract void bar();
}
A quick implementation :
public class Foo extends AbstractFoo {
#Override
void bar() {}
}
Now, in another package :
public static void main(String[] args) throws Exception{
AbstractFoo something=new Foo();
something.bar();//Compiler complains here
Compiler complains about visibility. So the default visibility for methods is package protected, even if the class is public abstract.
The Java Language Specification for Java 7 does not mention separate rules for abstract methods, as such an abstract method without a qualified access level is default aka package private, just like a normal method would have been.
See also 6.6.1. Determining Accessibility:
A member (class, interface, field, or method) of a reference (class, interface, or array) type or a constructor of a class type is accessible only if the type is accessible and the member or constructor is declared to permit access:
If the member or constructor is declared public, then access is permitted.
All members of interfaces are implicitly public.
Otherwise, if the member or constructor is declared protected, then access is permitted only when one of the following is true:
Access to the member or constructor occurs from within the package containing the class in which the protected member or constructor is declared.
Access is correct as described in §6.6.2.
Otherwise, 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.
Otherwise, we say there is default access, which is permitted only when the access occurs from within the package in which the type is declared.
(emphasis mine)
Also note that the term 'default access' is equivalent to 'package private', the only 'exception' to this is method declarations in an interface, which simply are always public and therefor don't need to be prefixed.
Edit:
As adenoyelle indicates in his answer, you can override a 'default' abstract method in a different package (as required by the rules in JLS 8.4.3.1. abstract Methods), as such you could consider them to be protected, but a quick scan of the JLS doesn't seem to make this explicit.
Edit 2:
I just tested it. It is impossible to implement an abstract class that has a method with default access in a different package. It simply does not compile. This shows that the method has default (package private) access, not protected. It also indicates that 8.4.3.1 doesn't actually require that it is always possible to implement an abstract method, just that it excludes nonsensical options like private abstract void method()
For example compiling:
package example;
public abstract class AbstractTest {
abstract void testMethod();
}
and
package example.sub;
import example.AbstractTest;
public class TestImpl extends AbstractTest {
void testMethod() {
//implemented
}
}
Leads to compile error:
example\sub\TestImpl.java:8: error: TestImpl is not abstract and does not override abstract method testMethod() in AbstractTest
public class TestImpl extends AbstractTest {
^
1 error
The default visibility is known as “package” (though you can't use this keyword), which means the field will be accessible from inside the same package to which the class belongs.
uf you declare as public than it will be public for all no matter its abstract or not
Default access modifier means we do not explicitly declare an access modifier for a class, field, method etc.
A variable or method declared without any access control modifier is available to any other class in the same package.
So there is no matter of the method is abstract or not .
The access level of the methods would remain as default(would be only visible within the package) even if the abstract class is of public access level. Only if the child class overrides the method with a pulbic access modifier, it would be visible outside the package.
You are on to something, just a bit off: in interfaces the default—and in fact the only choice— is public. In all classes the default is the same, which is package-private.
Even if the subclass "tries" to override the method with "default" access defined in the abstract class in the subclass with "public" access, compiler still complains that chap6.AbstractImpl is not abstract and does not override abstract method getHelp() in random.AbstractLearner.
So, in effect the compiler error message is really misleading here because there is no way that this can be fixed unless the access specifier for the getHelp() method in the AbstractLearner is changed to public.
package random;
public abstract class AbstractLearner {
abstract void getHelp();
}
package chap6;
import random.AbstractLearner;
public class AbstractImpl extends AbstractLearner {
public void getHelp() {
System.out.println("Hello");
}
}

Categories