I'm refactoring an android project which is getting to big. Running lint gives me the JSME issue Private member access between outer and inner classes. Considering the following example
public class Outer {
private Inner mInner = new Inner();
private class Inner {}
}
I get the information
Name
private field Inner mInner
Location
class Outer (default package)
Problem synopsis
Access to private member of class 'Inner' at line 2
Problem resolution
Make 'Inner' constructor package-local
Applying the problem resolution changes the source to
public class Outer {
private Inner mInner = new Inner();
private class Inner {
Inner() {}
}
}
I'm a little confused at the moment. Until now I thought the example would be equivalent to
public class Outer {
private Inner mInner = new Inner();
private class Inner {
public Inner() {}
}
}
Am I wrong in this case or is it an issue of lint?
Section 8.8.9 of the Java language specification, "Default constructor" says:
In a class type, if the class is declared public, then the default
constructor is implicitly given the access modifier public (§6.6); if
the class is declared protected, then the default constructor is
implicitly given the access modifier protected (§6.6); if the class is
declared private, then the default constructor is implicitly given the
access modifier private (§6.6); otherwise, the default constructor has
the default access implied by no access modifier.
You're wrong in your understanding, but the linter is not being particularly clear, and the advice probably isn't relevant for Android (which isn't J2ME).
As David explained, the inner class's implicit default constructor has the same access modifier as the class itself, but private members are accessible within the same compilation unit (Java file). There's no language reason to avoid the private constructor.
However, internally, since the classes are compiled into separate output files, the compiler has to create synthetic adapter methods to provide the classes access to the private members. The runtime disadvantage of these methods is irrelevant for desktop applications, but for something as cramped as J2ME, the difference might be worth eliminating by making the member accessible directly (using package scope).
Android performs significant post-processing on the class files, and Android devices are not nearly as constrained as J2ME devices. Unless you're writing code to target both platforms, I'd change the lint configuration.
Related
Consider the following code:
public class Foo
{
class Bar
{
private String barbar;
public Bar( String b ) { barbar = b; }
}
class Meh
{
Bar b = new Bar("BAR!");
public void displayName() {
System.out.println( b.barbar );
}
}
}
Java allows class Meh to access the private instance variable, barbar, which is declared as private within Bar.
I know this question has been asked before here. However, the answer just basically reiterates that the observed scope is what it is (that barbar is accessible within the braces of class Foo), but offers no explanation. After some Googling, I hadn't been able to land on a good discussion of this behavior. What I would like to know is if there's a specific rationale for this scoping behavior. I would have expected barbar to be private "within the braces" of class Bar.
The fundamental aspect of this is that inner classes (as opposed to static nested classes) are part of their enclosing class. They aren't separate from it, or from each other. So just like other parts of the enclosing class (constructors and methods) have access to all of its private information, so do all the members of the inner classes. Inner classes are, in some sense, a bit of a fiction that we use as a convenient abstraction mechanism. And since inner classes are part of the enclosing class, their private information is its private information, and so is shared with other inner classes.
As per the JLS
The body of a class declares members (fields and methods and nested
classes and interfaces), instance and static initializers, and
constructors. The scope of a member is the
entire body of the declaration of the class to which the member
belongs. Field, method, member class, member interface, and
constructor declarations may include the access modifiers
public, protected, or private.
So, as per the example, class Bar and class Meh both belongs class Foo.
Hence, any member of Foo will have access to all its other members regardless its a field method or nested class/interface as per the first line in the specs.
Comment questioin:
If I made the variable within Bar to be public instead of private would it then become accessible outside of Foo even though it's from an inner class?
Yes of course it is accessible from outside of the class.
Look at the below example
class A {
Foo foo = new Foo();
Foo.Bar bar = foo.new Bar("name");
public void someOtherMethod() {
bar.barbar = "someOtherName";
//this is valid only if barbar is public
//or if it is default and within the same package
}
}
The inner class is just a way to cleanly separate some functionality that really belongs to the original outer class. They are intended to be used when you have 2 requirements:
Some piece of functionality in your outer class would be most clear if it was implemented in a separate class.
Even though it's in a separate class, the functionality is very closely tied to way that the outer class works.
Given these requirements, inner classes have full access to their outer class. Since they're basically a member of the outer class, it makes sense that they have access to methods and attributes of the outer class -- including privates.
I don't think you're going to find the rationale behind the design of the language unless you talk to the people who designed it. However, one can often get insight into the "why" through the Java Language Specification. Section 6.3 (Scope of a Declaration) lays out the rules for scoping and has examples of the rules in action.
Specifically, the behavior you're asking about is covered by the statement:
The scope of a local class declaration immediately enclosed by a block
is the rest of the immediately enclosing block, including its own
class declaration.
Your two local classes have the same scope, i.e. the "immediately enclosing block", specifically class Foo.
Perhaps in your question you can explain why you think barbar should be inaccessible to the enclosing class, and then we can address any misconceptions you have about how Java works.
We all know that if we don't specifically define a constructor, the compiler inserts an invisible zero-parameter constructor. I thought its access modifier was public, but in dealing with an inner class issue, I found maybe I was wrong. Here is my code:
public class Outer {
protected class ProtectedInner {
// adding a public constructor will solve the error in SubOuterInAnotherPackage class
//public ProtectedInner() {}
}
}
And there is a subclass of Outer in another package:
public class SubOuterInAnotherPackage extends Outer {
public static void main(String[] args) {
SubOuterInAnotherPackage.ProtectedInner protectedInner
= new SubOuterInAnotherPackage().new ProtectedInner(); // Error!! Can't access the default constructor
}
}
You will get an error in the main() method, but if you add a public constructor to the ProtectedInner class, that error is solved. That's why I'm thinking that the modifier of the default constructor is not public! So could anyone tell me what the access modifier of the default constructor is?
I thought its access modifier is public, but when I deal with a inner class issue, I found maybe I was wrong.
Yup. Indeed, I found myself in the same situation a couple of years ago. I was surprised by an error (through Guice injection, which made it slightly harder to find).
The key is to check the spec, in this case section 8.8.9:
In a class type, if the class is declared public, then the default constructor is implicitly given the access modifier public (§6.6); if the class is declared protected, then the default constructor is implicitly given the access modifier protected (§6.6); if the class is declared private, then the default constructor is implicitly given the access modifier private (§6.6); otherwise, the default constructor has the default access implied by no access modifier.
So in this case, your constructor is implicitly protected.
In addition to what Jon pretty well stated, here is an image example, for the visual guys.
If there is no constructor in a class, compiler automatically creates a default constructor.
Here is an example that successfully depicts the above rule:
For further reference, please refer here.
I would like to point out one more thing that I recently got.
If you define a default constructor for your class then it's acess specifier will be what you assign. For example,
public class A{
A(){
// do some stuff
}
}
Here the access specifier of the default constructor is package access and not public access (that of the class).
However
public class A{
// no constructor is defined
}
Here the compiler will sympathize with you and give you a default constructor whose access specifier will be same as the class , that is public.
Why inner class have access to private field of input object param of such class?
private class Inner {
private int privatefield = 0;
public void method( Inner inner ) {
privatefield = 1; //ok
inner.privatefield = 1; //this line confusing me (broken incapsulation)
}
}
This has nothing to do with inner classes per se.
The code within a class always has access to private members of the same class in Java. The notion of private/public etc refers to where the code lives, not whether it refers to "this" instance or a different one. Protected access is a little bit more complicated than other access, but the same general principle applies.
See JLS section 6.6 for more details. In particular:
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.
And that explains why one nested class has access to another nested class's private fields, so long as they're nested within the same top-level class, as per your comments. It's an odd rule, I agree - and I prefer C#'s approach in terms of private accessibility and nested types - but the above quote shows that the compiler is obeying the rules of the JLS.
I suspect the reasoning is that if you're working within the same top-level class, you're responsible for all the code within that class, including the nested classes - so you're trusted not to abuse them.
Why inner class have access to private field of input object param of such class?
Even though private, inner class is a member of outer class
private members are accessible for the class members.
Any method within a class can access a global variable inside that class. The private parameter only makes it so that OTHER classes can not access the variable.
In this case the methods in Inner will be able to find the var and methods outside of Inner will not be able to.
If you see Docs of Controlling Access to Members of a Class
The private modifier specifies that the member can only be accessed in its own class.
Since privatefield is member of your class, you have access inside the class.
Since the method(Inner inner) resides in the Inner class, hence method(Inner inner) can access the member private to the Inner class.
Access modifiers define the scope in which your variable can be accesses and not whether individual instances can access it or not!
private int privatefield = 0;
Your variable is non static so it will be accesses by instance of the class. So inner.privatefield makes sense. Now your variable privatefield is private which means you can access it inside your Inner class. Since your are accessing the variable inside method() which is inside Inner class there is no problem and is perfectly valid.
Consider that your class isn't nested neither private, cause it's irrelevant with your question. It has to do with scope visibility.
Declaration says
private modifier — the field is accessible only within its own class.
Since you access the privatefield property inside the class that is declared even if it's a member of a different instance your encapsulation isn't broken,private scope is maintained.
Why does Java not allow a top level class to be declared as private? Is there any other reason other than "We can't access a private class"?
A top-level class as private would be completely useless because nothing would have access to it.
Java doesn’t allow a top level class to be private. Only 'public' or 'package'.
I believe a better question would be:
What would it mean for a top level class to be private?
If you think in terms of access levels, the level above class is package. In fact you can have package private top level classes in Java! Taking from the Oracle (formerly Sun) Java tutorials:
If a class has no modifier (the default, also known as package-private), it is visible only within its own package (packages are named groups of related classes — you will learn about them in a later lesson.)
Depending on the answer to the question I asked, this might fit with your definition of a "top level private class".
Private classes are allowed, but only as inner or nested classes. If you have a private inner or nested class, then access is restricted to the scope of that outer class.
If you have a private class on its own as a top-level class, then you can't get access to it from anywhere. So it does not make sense to have a top level private class.
In theory, you could instantiate and call methods on a private top-level class (if such a thing were allowed by the language ... which it isn't!), but you would have to use reflection to do this. Sensibly (IMO) Sun decided that private top-level classes were not a good thing to support at the language level.
Actually, it is possible that the JVM might support top-level private "classes" created by bytecode magic. But it is not a useful thing to do.
UPDATE - In fact, the current JVM spec makes it clear that the ACC_PRIVATE bit of the access flags word for a class is "reserved for future use", and that Java implementations should treat it as unset. Thus, the above speculation is moot for any JVM that strictly implements the current specification.
We can not declare an outer class as private. More precisely, we can not use the private access specifier with an outer class. As soon as you try to use the private access specifier with a class you will get a message in Eclipse as the error that only public, final, and abstract can be used as an access modifier with a class.
Making a class private does not make any sense as we can not access the code of its class from the outside.
There would be no way to access that class or its members.
This needs a simple understanding of why it is not required to declare classes as private in Java.
If your class is itself a standalone program/software (which is highly unlikely) then you would have already defined it in a project and a package specific for it. So adding the private keyword is redundant to it.
If that's not the case then default access is then it means your program/software depends on different classes to run. If we are declaring one class among them as private (in case if we could) then we are restricting its accessibility by other classes, etc. which isn't of any use. It simply means that the class declared as private by us isn't of any use for the code to run. Which again renders it useless.
If you mean package level access then for default access we don't need to declare any keyword before it.
As we already know, a field defined in a class using the private keyword can only be accessible within the same class and is not visible to the outside world.
So what will happen if we will define a class private? Will that class only be accessible within the entity in which it is defined which in our case is its package?
Let’s consider the below example of class A
package com.example;
class A {
private int a = 10;
// We can access a private field by creating object of same class inside the same class
// But really nobody creates an object of a class inside the same class
public void usePrivateField(){
A objA = new A();
System.out.println(objA.a);
}
}
Field ‘a’ is declared as private inside ‘A’ class and because of it, the ‘a’ field becomes private to class ‘A' and can only be accessed within ‘A’. Now let’s assume we are allowed to declare class ‘A’ as private, so in this case class ‘A’ will become private to package ‘com.example’ and will not be accessible from outside of the package.
So defining private access to the class will make it accessible inside the same package which the default keyword already does for us. Therefore there isn't any benefit of defining a class private; it will only make things ambiguous.
You can read more in my article Why an outer Java class can’t be private or protected.
You cannot define a top level class private (or anything else besides public). You will get a compilation error.
Something.java:6: error: modifier private not allowed here
private class Something {
^
1 error
You have only two options, public or no access modifier at all. By omitting public you, implicitly, limit class access to within the package (aka: package-private).
Is the private member access at the class level or at the object level. If it is at the object level, then the following code should not compile
class PrivateMember {
private int i;
public PrivateMember() {
i = 2;
}
public void printI() {
System.out.println("i is: "+i);
}
public void messWithI(PrivateMember t) {
t.i *= 2;
}
public static void main (String args[]) {
PrivateMember sub = new PrivateMember();
PrivateMember obj = new PrivateMember();
obj.printI();
sub.messWithI(obj);
obj.printI();
}
}
Please clarify if accessing the member i of obj within the messWithI() method of sub is valid
As DevSolar has said, it's at the (top level) class level.
From section 6.6 of the Java Language Specification:
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.
Note that there's no indication that it's restricted to members for a particular object.
As of Java 7, the compiler no longer allows access to private members of type variables. So if the method had a signature like public <T extends PrivateMember> void messWithI(T t) then it would be a compiler error to access t.i. That wouldn't change your particular scenario, however.
Note that you don't even need source level access to mess with private fields. By using java.lang.reflect.AccessibleObject.setAccessibe(), all code can access all private members of all other code unless you specify a security policy that disallows it.
private is not by itself a security feature! It is merely a strong hint to other developers that something is an internal implementation detail that other parts on the code should not depend on.
Neither. Private access is scoped to the enclosing top-level class, so you can access private members of different class in the same top-level class:
class PrivateAccess {
static class InnerOne {
private int value;
}
static class InnerTwo {
int getOne ( InnerOne other ) {
return other.value;
}
}
}
The usual meaning of class access means that you have access to privates of other instances of the same type. In Java, private access is determined lexically, not by type.
Class level. The idea is that the code of a class (but nothing else) knows how to handle objects of that class.
If you have access to the class source code anyway, there is little sense in "hiding" anything from you.
As others have stated, private, default access ("package private"), protected and perhaps in JDK 7 module are class based (there are very strange rules for nested classes inheritance that I can't remember). But why?
Primarily it's down to methods that act as binary (or more) operators. For efficient implementation they often require or are easier to write without having to use or modify the public API. Have a look through at implementations of equals - in good code you'll find direct access of fields with few method calls to this. (The performance aspect of this is now mostly irrelevant with modern JVMs inlining common calls, but the code quality issue is still there.)
Just to add to DevSolar's answer, I would expect messWithI to be declared static as such:
public static void messWithI(PrivateMember t) {
t.i *= 2;
}
I had a hard time even reading what it is that you were trying to do without the 'static' hint... And it also makes it easier to answer your original question -- which is that private members are not limited in scope to just the instance in question.
The same page says, in sub-section 6.6.8, you can also find the following statement:
A private class member or constructor is accessible only within the body of the top level class that encloses the declaration of the member or constructor. It is not inherited by subclasses.
The private class member whose access we evaluate here is i.
public void messWithI() is a method that exists within the body of the top level class where i has been declared, which is, precisely, PrivateMember.
Your construct meets the statement above, and that is why it runs without problems.
Thas is another way to say the same as Jon and Devsolar.
Access modifiers for class members are related to where the code is written, (in which package, and in which class), regardless of what kind of member the access gets granted: a class member or an instance member.
Logically, you cannot use an instance member of a class if you do not have an instance of the class, but that is a different issue, related to the life-cycle of the member.