Java: accessing protected fields from inner class - java

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.

Related

Generate inner class

my annotation processor reads a class like this:
#Foo
public class Bar (){
}
Now I want to generate an inner class Bar$MyGeneratedClass so that at the end I have a class MyGeneratedClass that to the compiler / jvm looks like this:
public class Bar (){
// Generated by annotation processor
public static class MyGeneratedClass () { ... }
}
Is this possible? I think so, I guess I just have to name the generated class Bar$MyGeneratedClass right?
Does anybody know how to generate such a inner class with java poet?
You can use javapoet to create new classes. It's not possible modify existing class with javapoet.
On jvm level there is no such things as inner classes.
So while compiling both classes (the inner and the outer) are transfomed to simulate that effect.
The inner class gets a constructor parameter. With that parameter you need to pass in an instance of the outer class.
As both class types can access private members package private accessors are created.
Especially the second transformation requires you to change the outer class.

Using two different classloaders to load two children of the same class

I have two classes, Child1 and Child2, children of the class Parent. The class Parenthas a static member, and the two children classes have a static method run() using this static member. As I have to call Child1.run() and Child2.run() inside the same application, I want that each of the child class can access its own version of the static member of the parent class.
I know that it would be easier to put the static member of the parent class into the children class, but I can't modify the source code of the classes.
I read around that I shoud use two different classloaders, but I don't understand how. Can someone explain me how to use classloaders to achieve the result I mentioned, maybe with some example code?
Yes, what you ask is possible using two different ClassLoaders. You need to compile your classes (Parent, Child1 and Child2), but they should not be in the same classpath location as the class you're invoking them from (call that class Main).
Instead, put them somewhere on your disk in a directory or put them in a jar file.
From that location, you can create multiple classloaders, and you can use the method loadClass to load classes with it; and you can use reflection to invoke methods, like the method run on it.
import java.lang.reflect.*;
import java.net.URL;
public class Main {
public static void main(String[] args) throws Exception {
URL childAndParentClassesLocation = new URL("c:/Somewhere/On/Your/Disk");
ClassLoader cl1 = new URLClassLoader(new URL[] { childAndParentClassesLocation }, Main.class.getClassLoader());
ClassLoader cl2 = new URLClassLoader(new URL[] { childAndParentClassesLocation }, Main.class.getClassLoader());
Class child1 = cl1.loadClass("packagename.Child1");
Class child2 = cl2.loadClass("packagename.Child2");
Method child1Run = child1.getMethod("run");
Method child2Run = child2.getMethod("run");
child1Run.invoke(null); // Invoke run method on Child1
child2Run.invoke(null); // Invoke run method on Child2
}
}
P.s. this is bad design for a general purpose application, but you didn't say you were making a general-purpose application. So I don't see any reason to dumb down SO by dissuading interesting questions that help explain how the JVM works.
Well, purpose of class level member/static member is to have one copy per JVM. what you are suggesting in your question is defeating the above purpose.
If that is the case, you should achieve it with instance level members. See this

Protected member behavior once it was inherited.

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
}
}

Java inner class with the same name as other top level class

I have question related to Java inner classes.
Is there a way to access top level class A from top level class Main that define inner class A?
Below is sample code demonstrating the problem:
class A { // Outer Class A
{
System.out.println("A outer");
}
}
class B { // Outer Class B
{
System.out.println("B outer");
}
}
public class Main {
class A { // Inner Class A
{
System.out.println("A inner");
}
}
public void newA() {
class A { // Local Class A
{
System.out.println("A local");
}
}
new A();
}
public static void main(String[] args) {
new Main().newA(); // prints "A local"
new Main().new A(); // prints "A inner"
//new A(); // compiler error: No enclosing instance of type Main is Accessible.
new B(); // but this works and prints "B outer"
}
}
Use the fully qualified class name:
new com.mypackage.A();
If your classes are in packages, I'd expect that to be the simplest way of achieving this. I don't believe there's any equivalent of the C# global:: for Java, to force this sort of thing.
Ultimately though, I'd just try to change the class names to avoid the problem happening in the first place. That's usually a better approach than using workarounds.
You should provide different package names for your two As so that they are clearly different types, then you can reference them by their full names.
For example:
my.package.for.toplevel.A outerA = new my.package.for.toplevel.A(); // prints "A outer"
// if this is the
// package
Or even better, use two different names for two distinct classes.
The name of the inner class is said to shadow the name of the top-level class. The top-level class cannot be referenced by its simple name in the scope of the inner class; the top-level class can only be referenced via a qualified name.
If the top-level class is in the default package (in which case its canonical name is the same as its simple name), you can still access it via reflection.
Yep, just use packages or avoid naming them the same from the beginning. I wonder what's your reason to name them the same anyway?
To explain your error, it comes from the fact that it tries to access the inner A, but since that class is not declared as static and there's no Main instance available, it can't create a non-static inner A that requires a reference to parent Main instance.
Assumed the classes are in the default package. To resolve naming conflict in this case needs either rename the conflicting classes or use the named package. Then use FQCN to resolve the issue.

Scope of protected members

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.

Categories

Resources