Подтвердить что ты не робот

Длина массива частного класса недоступна

Обратите внимание на следующий код:

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

Как я написал в коде. a.arr.length; дает ошибку.

Я действительно понимаю, почему это происходит. Это связано с тем, что подкласс B является закрытым. Но все же, почему это происходит. В классе A свойство arr было доступно, но почему бы и нет. Есть ли объяснение этому в jls или где-нибудь.

Я просто хочу дать четкое объяснение этому поведению. Я знаю, что личные вещи не могут быть доступны за пределами своего класса. Но публичный массив может быть. Независимо от того, какой он тип. И если что-либо доступно снаружи, необходимо также получить доступ к его публичным свойствам. Но здесь этого не происходит.

Изменить: Я обнаружил, что в С# даже невозможно создать массив частного класса. В java, если мы не можем получить доступ к чему-либо, и даже не можем знать длину массива частного класса, тогда используется создание массива частного класса.

4b9b3361

Ответ 1

Причиной этого является комбинация двух операторов в JLS:

  • Пункт 6.6.1 Определение доступности:

    Тип массива доступен тогда и только тогда, когда доступен его тип элемента.

    Это означает, что если T является закрытым, T[] также считается закрытым.

  • Пункт 10.7 Члены массива:

    Поле public final length, которое содержит количество компонентов массива. длина может быть положительной или нулевой.

Помните, что доступность определяется во время компиляции на основе типа ссылки, а не типа фактического объекта!

Теперь давайте рассмотрим более подробный пример, чтобы продемонстрировать, что это значит. Я добавил toString() и конструктор в 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 + ")";
        }

    }
}

Теперь, в классе C, мы используем:

A a = new A();
Object plain = a.plain;
String s = plain.toString();

Это законно, потому что a.plain - видимое поле. s будет содержать Hidden class B(99). Но если вы попробуете:

String s = a.plain.toString(); // Compile error

Это не будет разрешено, поскольку althogh toString() in B является общедоступным, сам B является закрытым, у вас нет доступа к его членам, будь то public или private.

Обратите внимание, что мы не можем получить доступ к i в B, несмотря на то, что он является общедоступным. Если мы используем:

plain.i

Тогда, поскольку i не является членом Object, вы получаете ошибку компиляции. И если мы используем:

a.plain.i

Тогда, поскольку a.plain является закрытым, вы не можете получить доступ к его членам, как мы уже пробовали.

Итак, переходим к вопросу о массивах. Предположим, что мы пишем:

Object[] objArr = a.arr;
int len = objArr.length;

Это законно, несмотря на то, что objArr является внутренним A.B[]. Мы имеем ссылку на Object[], Object является общедоступным, и поэтому Object[]. Но:

int len = a.arr.length;

Дает вам ошибку компиляции точно так же, как мы получили для a.plain.toString(). Хотя length является общедоступным, вы получаете доступ к нему через ссылку на A.B[]. A.B[] недоступен, поскольку A.B недоступен. И поэтому, поскольку length является его членом, вы не можете получить к нему доступ. Вы просто не можете получить доступ к любому члену ссылочного типа, который не отображается вам, по первому правилу выше.

Интересно отметить, что следующая легальная:

Object firstItem = a.arr[0];

Мы можем использовать выражение a.arr[0], поскольку оно не считается попыткой доступа к элементу массива. Элементы массива не считаются членами в нем. a.arr[0] - просто выражение в ссылке на массив, которая разрешает вводить A.B. Нет никаких проблем с таким выражением, пока мы не пытаемся получить доступ к элементам элемента.

firstItem.toString() // Good
a.arr[0].toString()  // Bad

Резюме

  • В порядке, чтобы получить привязку к типу private, при условии, что вы применили его к некоторому публичному супертипу.
  • Это нормально, чтобы получить конкретный элемент в массиве частного типа. Индексирование массива не считается "доступом к члену", это просто выражение в ссылке, которое дает ссылку на его тип члена. Что вам нужно будет использовать для чего-то публичного для использования.
  • Не удается попробовать получить доступ к члену с заданной ссылкой на закрытый тип, даже если этот элемент является общедоступным. Это включает в себя length массива.
  • Это нормально, чтобы получить доступ к этому публичному пользователю с помощью приведения к супертипу, если он доступен в этом супертипе. length доступен в Object [], поэтому вы можете получить это через.
  • Невозможно получить доступ к публичному члену частного типа, который не существует в доступном супертипе.

Ответ 2

Сделайте это:

 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();
    }
}

Согласно JavaDocs

На уровне участника вы также можете использовать публичный модификатор или модификатор (private-package), как и классы верхнего уровня, и с тем же значением. Для участников есть два дополнительных модификатора доступа: частный и защищенный. Частный модификатор указывает, что к члену можно получить доступ только в своем классе. Защищенный модификатор указывает, что к члену можно получить доступ только в пределах своего собственного пакета (как и для private-private) и, кроме того, подкласса его класса в другом пакете.

Ответ 3

Знает, что речь идет о доступе к полю length. Но мне было интересно узнать, что length можно определить с помощью расширенного для цикла, а не путем внесения изменений в привилегии доступа или использования отражения:

int length = 0;
for(Object o : a.arr) {
    length++;
}

Несколько интересных утверждений о массивах:

Массивы

В языке программирования Java массивы являются объектами (§4.3.1), являются динамически созданный и может быть назначен переменным типа Object (§4.3.2). Все методы класса Object могут быть вызваны в массиве.

Типы массивов

Длина массива не является частью его типа.