Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

length of array of private class is not accessible

Tags:

java

arrays

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.

like image 904
afzalex Avatar asked Sep 16 '15 10:09

afzalex


2 Answers

The reason for this is a combination of two statements in the JLS:

  1. 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.

  2. Item 10.7 Array members:

    The public final field length, 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

  • It's OK to get hold to a reference to a private type, provided you cast it to some public supertype.
  • It's OK to get a specific item in an array of a private type. Indexing the array is not considered "accessing a member", it's just an expression on a reference that gives you a reference to its member type. Which you'll need to cast to something public in order to use.
  • It's not OK to try accessing a member with a given reference to a private type, even if the member is public. This includes the length of an array.
  • It's OK to access that public member through a cast to a supertype if it's available in that supertype. length is available in Object [] so you can get it through that.
  • It's not possible to access a public member of a private type that doesn't exist in an accessible supertype.
like image 154
RealSkeptic Avatar answered Oct 07 '22 15:10

RealSkeptic


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.

like image 20
Michele Lacorte Avatar answered Oct 07 '22 14:10

Michele Lacorte