Reputation: 8652
Please consider the following code :
class A {
B[] arr = new B[10];
private class B {}
}
class C {
void fun(){
A a = new A();
Object arr = a.arr;
Object len = a.arr.length; // !! ERROR
}
}
As I written in code. a.arr.length;
is giving error.
I actually understand why it is happening. It is because sub class B
is private. But still why it is happening. In class A, property arr
was accessible, but why not it's length. Is there any explanation for this in jls or anywhere.
I just want a clear explanation for this behaviour. I know private things cannot be accessed outside of its class. But a public array could be. No matter of what type it is. And if anything is accessible outside, then its public properties should also be accessed. But here it is not happening.
Edit : I found that in C# it is not even possible to create an array of private class. In java if we cannot access anything, and cannot even know the length of the array of private class then what is the use of creating an array of private class.
Upvotes: 15
Views: 2594
Reputation: 10555
Knows that the question is about accessing the length
field. But, it was interesting for me to find that the length
can be determined by enhanced-for-loop, not by making changes to access privileges or using reflection:
int length = 0;
for(Object o : a.arr) {
length++;
}
Few interesting statements about arrays were:
In the Java programming language, arrays are objects (§4.3.1), are dynamically created, and may be assigned to variables of type Object (§4.3.2). All methods of class Object may be invoked on an array.
An array's length is not part of its type.
Upvotes: 1
Reputation: 34648
The reason for this is a combination of two statements in the JLS:
Item 6.6.1 Determining accessibility:
An array type is accessible if and only if its element type is accessible.
This means that if T
is private, T[]
is also considered private.
Item 10.7 Array members:
The
public final
fieldlength
, which contains the number of components of the array. length may be positive or zero.
Remember that accessibility is determined at compile-time based on the type of reference you have, not on the type of the actual object!
Now, let's go into a little more elaborate example to demonstrate what this means. I added a toString()
and a constructor to B
.
class A {
B[] arr = { new B(1), new B(2), new B(3), new B(4) };
B plain = new B(99);
private class B {
public int i;
B(int i) {
this.i = i;
}
@Override
public String toString() {
return "Hidden class B(" + i + ")";
}
}
}
Now, in class C, we use:
A a = new A();
Object plain = a.plain;
String s = plain.toString();
This is legal, because a.plain
is a visible field. s
will contain Hidden class B(99)
. But if you try:
String s = a.plain.toString(); // Compile error
This will not be allowed, because althogh toString()
in B
is public, B
itself is private, you have no access to its members, whether public or private.
Note that we cannot access i
in B
despite its being public. If we use:
plain.i
Then since i
is not a member of Object
, you get a compile error. And if we use:
a.plain.i
Then since a.plain
is private, you can't access its members, as we already tried.
So now we go and look at the issue of arrays. Suppose we write:
Object[] objArr = a.arr;
int len = objArr.length;
This is legal, despite the fact that objArr
is internally A.B[]
. We have a reference to Object[]
, Object
is public and so is Object[]
. But:
int len = a.arr.length;
Gives you a compile error exactly as we got for a.plain.toString()
. Although length
is public in itself, you are accessing it through a reference to A.B[]
. A.B[]
is not accessible because A.B
is not accessible. And therefore, because length
is its member, you cannot access it. You simply cannot access any member of a reference type that is not visible to you, by the first rule above.
It is interesting to note that the following is legal:
Object firstItem = a.arr[0];
We can use the expression a.arr[0]
because it is not considered an attempt to access a member of the array. The elements of the array are not considered to be members in it. a.arr[0]
is simply an expression on an array reference that resolves to type A.B
. There is no problem with such an expression as long as we don't try to access members of the item.
firstItem.toString() // Good
a.arr[0].toString() // Bad
Summary
length
of an array.length
is available in Object []
so you can get it through that.Upvotes: 5
Reputation: 5363
Do this:
class A {
B[] arr = new B[10];
public int getArrayLength()
{
return arr.length;
}
private class B {}
}
class C {
void fun(){
A a = new A();
Object arr = a.arr;
//Object isn't type safe
//Object len = a.getArrayLength();
int len = a.getArrayLength();
}
}
According to JavaDocs
At the member level, you can also use the public modifier or no modifier (package-private) just as with top-level classes, and with the same meaning. For members, there are two additional access modifiers: private and protected. The private modifier specifies that the member can only be accessed in its own class. 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.
Upvotes: 3