User zor07
zor07
31 уровень
Санкт-Петербург

Уровень 24. Ответы на вопросы к собеседованию по теме уровня

Статья из группы Архив info.javarush.ru
Уровень 24. Ответы на вопросы к собеседованию по теме уровня - 1
  1. Во что компилируются анонимные внутренние классы?

    Анонимные внутренние классы компилируются в файлы внешнийКласс$n.class. На месте внешнего класса, соответственно, название обрамляющего класса, внутри которого описывается анонимный внутренний класс. На месте n число от 1 до количества анонимных классов.

  2. Можно ли наследовать внутренние классы?

    Наследовать внутренние классы от других — можно.

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

    
    //: innerclasses/InheritInner.java
    // Наследование от внутреннего класса.
     
    class WithInner {
      class Inner {}
    }
     
    public class InheritInner extends WithInner.Inner {
      //! InheritInner() {} // He компилируется 
      InheritInner(WithInner wi) {
        wi.super();
      }
      public static void main(String[] args) {
        WithInner wi = new WithInner();
        InheritInner ii = new InheritInner(wi);
      }
    }
    

    Здесь класс InheritInner расширяет только внутренний класс, а не внешний. Но когда дело доходит до создания конструктора, предлагаемый по умолчанию конструктор не подходит, и вы не можете просто передать ссылку на внешний объект. Необходимо включить в тело конструктора выражение ссылкаНаОбъемлющийКласс.super(); в теле конструктора. Оно обеспечит недостающую ссылку, и программа откомпилируется.

  3. Можно ли наследовать анонимные внутренние классы?

    Описывая анонимный класс мы уже наследуемся от какого-то класса или реализуем какой-либо интерфейс. К анонимным классам напрямую нельзя применить слова extends или implements, но ведь никто не мешает заранее подготовиться и расширить нужный интерфейс, который будем реализовывать с помощью анонимного класса. Пример в коде ниже.

    
    import java.awt.event.WindowListener;
    
    public class TestExtendAnonym {
        private interface MyInterface extends Runnable, WindowListener {
        }
    
        Runnable r = new MyInterface() {
        ...
        //Пример того как реализовать 2 и более интерфейса в анонимном классе
        };
    }
    

    Наследоваться от анонимного класса нельзя.

  4. Можно ли переопределять внутренние классы?

    Переопределение внутреннего класса, как если бы он был еще одним методом внешнего класса, фактически не имеет никакого эффекта:

    
    //: innerclasses/BigEgg.java
    // Внутренний класс нельзя переопределить 
    // подобно обычному методу,
    import static net.mindview.util.Print.*;
     
    class Egg {
      private Yolk y;
      protected class Yolk {
        public Yolk() { print("Egg.Yolk()"); }
      }
      public Egg() {
        print("New Egg()");
        y = new Yolk();
      }
    }	
     
    public class BigEgg extends Egg {
      public class Yolk {
        public Yolk() { print("BigEgg.Yolk()"); }
      }
      public static void main(String[] args) {
        new BigEgg();
      }
    }
    

    Вывод:

    
    New Egg()
    Egg.Yolk()
    

    Конструктор по умолчанию автоматически синтезируется компилятором, а в нем вызывается конструктор по умолчанию из базового класса. Можно подумать, что при создании объекта BigEgg должен использоваться «переопределенный» класс Yolk, но это отнюдь не так, как видно из результата работы программы.

    Этот пример просто показывает, что при наследовании от внешнего класса ничего особенного с внутренними классами не происходит. Два внутренних класса — совершенно отдельные составляющие, с независимыми пространствами имен. Иными словами нельзя.

  5. Какие ограничения есть у локальных классов?

    Вначале вспомним что такое локальный класс. Это класс, описанный в блоке кода, то есть, по-простому — между кавычек {}. Наиболее часто эти кавычки являются телом метода. Но могут они быть и просто блоком, статическим блоком, телом if-ов, циклов и т.д.

    Локальный класс наделён особенностями внутренних классов, но имеет отличительные черты, а именно:

    1. он имеет доступ только к финальным полям и аргументам обрамляющего метода, а также ко всем полям обрамляющего класса, в том числе приватным и статическим;
    2. локальный класс виден и может создаваться только в блоке, в котором описан;
    3. у локального класса не ставится модификатор доступа;
    4. не может иметь статических полей, методов, классов (за исключением финальных);
    5. локальный класс, объявленный в статическом блоке может обращаться только к статическим полям внешнего класса.

    Но! Начиная с Java8 мы можем обращаться в локальных классах к не финальным локальным переменным, если они не были изменены до момента инициализации класса. Также теперь стало возможным обращение к не финальным параметрам метода.

  6. Может ли анонимный внутренний класс содержать статические методы?

    Нет. У Анонимных внутренних классов, как и у внутренних классов не может быть статических полей, методов. (вспомним, что анонимные классы компилируются в обычные внутренние, а те, в свою очередь, связаны с объектом обрамляющего класса)

  7. Можно ли создать объект внутреннего класса, если у внешнего класса только private конструктор?

    Имея подобный код:

    
    public class PrivateConst {
        private PrivateConst() {}
        public class InnerClass{
            public void f(){
                System.out.println("hello");
            }
       }
    }
    

    Напрямую, в другом классе (вне обрамляющего), конечно, создать объект InnerClass следующим способом не получится:

    
    PrivateConst.InnerClass priv = new PrivateConst().new InnerClass();
    

    Но! Что если у нас есть метод, возвращающий экземпляр

    
    PrivateConst:public class PrivateConst {
        private static PrivateConst instance;
        private PrivateConst() {}
    
        public static PrivateConst getInstance(){
            instance = new PrivateConst();
            return instance;
        }
    
        public class InnerClass{}
    }
    

    В этом случае приватный конструктор нам не помеха для создания объекта InnerClass. Так же мы без проблем сможем создавать его в методах и в других внутренних классах, принадлежащих PrivateConst. Ответ — можно, если каким-либо способом нам удастся получить объект обрамляющего класса.

  8. Можно ли объявлять внутренние классы private?

    Да, можно.

    PS Обоснования так и не нашел, но на философии java встречались подобные примеры. Плюс IDE не ругается. Буду признателен за обоснование, но предположу, что в этом плане внутренний класс ничем не отличается от обычного класса.

  9. Можно ли объявлять анонимные внутренние классы private?

    Аналогично (в плане не нашел обоснования). Можно объявить private переменную от типа которой наследуется наш анонимный класс.

  10. Сколько у класса максимально может быть внутренних классов?

    Сколь угодно много. Ограничение особенности ОС и длинны имени файлов.

Комментарии (14)
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION
Igor Уровень 36 Минск Беларусь
17 июня 2021
Круто, узнал что в Java есть множественное наследование. Но только в интерфейсах.🤦‍♂️

private interface MyInterface extends Runnable, WindowListener 
Сергей Уровень 38
26 сентября 2020
Расскажите людям в конце концов, что для JVM нет разницы между внешними и внутренними классами, нет отдельного синтаксиса для внутреннего (вложенного) класса. Оба класса абсолютно равноценны, они не находятся один в другом. Компилятор автоматически видоизменяет эти классы, добавляя, например, в конструктор внутреннего класса параметр - ссылку на объект внешнего класса. А для доступа к private-полю внешнего класса добавляет во внешний класс статический метод типа access$0(OuterClass o), потом обращение к этому полю во внутреннем классе заменяет на вызов этого метода и т.д.
Nordis Уровень 28 Санкт-Петербург Expert
21 октября 2019
Что это такое? - объемлющего.
Victor Уровень 35 Минск Беларусь
9 октября 2018
>Но! Начиная с Java8 мы можем обращаться в локальных классах к не финальным локальным >переменным, если они не были изменены до момента инициализации класса. Также теперь стало >возможным обращение к не финальным параметрам метода. не совсем верно, поля по сути остаются final (effective final), просто ключевое слово не обязательно добавлять.
lichMax Уровень 40 Санкт-Петербург Россия
19 апреля 2017
По поводу восьмого вопроса: по-моему это довольно часто встречаемая практика. Просто создаётся класс для внутреннего пользования. Ну либо просто скрывается реализация этого класса, либо контролируется получение объектов этого класса, а также его тип (примеры была в «Философии Джава» Эккеля).
Slavaslav Уровень 34 Киев
16 сентября 2016
1. Во что компилируются анонимные внутренние классы?

9. Можно ли объявлять анонимные внутренние классы private?
Grif Уровень 11 Латвия
28 июня 2016
8. Можно ли объявлять внутренние классы private? 9. Можно ли объявлять анонимные внутренние классы private? Источник Поскольку статические вложенные классы сами являются членами класса, то статический вложенный класс может быть объявлен с любыми модификаторами доступа. Эти модификаторы имеют одно и то же значение для статических вложенных классов и для остальных членов класса. Вложенный класс отличается от не вложенного(класса члена) наличием/отсутствием ссылки на обрамляющий класс, но это ни как не влияет на модификаторы доступа. Для локального класса действуют правила блока кода, т.е. модификаторы не объявляются.
Анонимный класс вообще не имеет возможности идентифицироваться по имени (раз уже его нет) т.е. мы не можем объявить анонимный класс таким способом, который позволит нам присвоить ему именно как классу какой-либо модификатор доступа… а вот переменной которой может быть присвоена ссылка на объект анонимного класса, модификаторы доступа присваиваются в зависимости от того, где такая переменная объявлена.