Не удается получить доступ к защищенному внутреннему классу при наследовании

Чтение через "Мышление в Java" я застрял в главе 6 из раздела "Внутренние классы".


Упражнение 6: (2) Создайте интерфейс, по крайней мере, с одним методом в своем собственном пакете. Создать класса в отдельном пакете. Добавьте защищенный внутренний класс, который реализует интерфейс. В третий пакет, наследовать от вашего класса и, внутри метода, вернуть объект защищенный внутренний класс, переход к интерфейсу во время возврата.


Это мой код:

IOne.java

интерфейс

package intfpack;
public interface IOne{
        void    f();
}

COne.java

Класс с защищенным внутренним классом, который реализует интерфейс

package classpack;
import intfpack.*;
public class COne{
        protected class Inner implements IOne{
                public void f(){System.out.println("Inner class of COne");}
        } 
}

CTwo.java

Наследование класса с защищенным внутренним классом

package thirdpack;
import classpack.*;
import intfpack.*;

public class CTwo extends COne{
        public IOne getInner(){
                IOne io = new Inner(); 
                return io;
        }
        public static void main(String[] args){
                CTwo ct = new CTwo();
                ct.getInner();
        }
}

Скотт говорит следующее:

javac CTwo.java
CTwo.java:9: Inner() has protected access in classpack.COne.Inner
                IOne io = new Inner(); 
                          ^
1 error

Но в книге говорится, что я могу получить доступ к защищенным внутренним классам в производном классе. Где ошибка?

Ответы

Ответ 1

Сообщение об ошибке жалуется на защищаемый конструктор, а не на класс. Но вы явно не определили конструктор в коде, который вы опубликовали. В этом случае в соответствии с JLS, конструктор по умолчанию будет защищен (тот же, что и класс).

Ответ 2

Вам нужно определить конструктор public для Inner class:

public class COne {

    protected class Inner implements IOne{

        public Inner() { }

        public void f(){System.out.println("Inner class of COne");}
    }
}

Ответ 3

Это ясно. Но здесь действительно странно.

Согласно JLS, если CTwo расширяет COne.Inner, ожидается, что он получит доступ к внутреннему защищенному конструктору, но на практике это не так., См. Ниже.

package p1;
public class COne {
    public static class Inner {
        protected Inner() {}
    }
}

package p2;
public class CTwo extends COne.Inner {
    public void getIface() {
        new COne.Inner();
        // Compile time error anyway with the same complains:
        // "Inner() has protected access in p1.COne.Inner"
        // ...unlike my expectations...
    }
}  

Ответ 4

Проблема не в классе Inner, а в Inheritance.

Сделайте несколько экспериментов.

Первый класс Inner и Outer имеют как полный доступ друг к другу. Таким образом, следующий код работает хорошо.

package com.ciaoshen.packA;

public class Outer {
        protected class Inner {
            public void foo() { System.out.println("Hello Ronald!"); }
        }
        protected Inner inner() {
            return new Inner();
        }
        public static void main(String[] args) {
            new Outer().inner().foo(); // Output: Hello Ronald!
        }
}

Теперь в другом пакете DerivedOuter выводится из Outer. DerivedOuter вызывает метод inner(), унаследованный от класса Outer. Он по-прежнему работает!

package com.ciaoshen.packB;

class DerivedOuter extends Outer {
    public static void main(String[] args) {
        new DerivedOuter().inner().foo(); // Output: Hello Ronald!
    }
}

Но , когда я переопределяю метод inner() в классе DerivedOuter, возникает такая же ошибка!

package com.ciaoshen.packB;

class DerivedOuter extends Outer {
    @Override
    public Inner inner() { // this is not the inner() of Outer class. BOOM!
        return new Inner();
    }
    public static void main(String[] args) {
        new DerivedOuter().inner().foo(); // ERROR: Outer.Inner() has protected access in Outer.Inner
    }
}

Заключение, защищенный встроенный конструктор доступен только в пределах исходного класса Outer. Любой дополнительный метод (например: ваш метод getInner()) не имеет доступа к защищенному конструктору Inner.

Ключевым моментом является то, что, когда DerivedOuter наследует от Outer, вы можете представить, что DerivedClass IS-A Outer, и он содержит класс Inner внутри как его член. Но на самом деле DerivedOuter не имеет прямого доступа к классу Inner, но работает только с помощью своего суперкласса Outer.