JavaRush/Java блог/Архив info.javarush/Уровень 24. Ответы на вопросы к собеседованию по теме уро...
zor07
31 уровень

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

Статья из группы Архив info.javarush
участников
Уровень 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. Сколько у класса максимально может быть внутренних классов?

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

Комментарии (22)
  • популярные
  • новые
  • старые
Для того, чтобы оставить комментарий Вы должны авторизоваться
Максим Li Java Developer
28 ноября 2023, 05:38
Стало чуточку понятнее.
Anonymous #3036451
Уровень 37
20 июня 2022, 09:25
Можно ли переопределять внутренние классы? Внутренний класс является просто полем наружного класса и, как любое другое не приватное поле, может быть затенен в классе наследнике. Метод класса знает только о переменных своего класса и ничего "не знает" о переменных класса наследника. Немного расширим пример:
class Egg {
    private Yolk y;
    protected class Yolk {
        public Yolk() {
            System.out.println("Egg.Yolk()"); }
    }
    public Egg() {
        System.out.println("New Egg()");
        y = new Yolk();
    }
}

public class BigEgg extends Egg {
    public class Yolk {
        public Yolk() {
            System.out.println("BigEgg.Yolk()"); }
    }

    public BigEgg() {
        Yolk z = new Yolk();
    }
    public static void main(String[] args) {
        new BigEgg();
    }
}
Выдаст:
New Egg()
Egg.Yolk()
BigEgg.Yolk()
Получается, контруктор класса Egg "знает" только о полях своего класса и создает объект Yolk из класса, который определен в нем. А конструктор класса BigEgg, создает объект Yolk из своего класса Yolk, который затеняет класс родителя. Поэтому, ИМХО, если под переопределением внутреннего класса понимать затенение поля класса в наследнике, то да, можно.
Skotique
Уровень 35
14 октября 2023, 09:26
можно. изуверский интеллект не даст соврать. Thus, the output shows that first, the constructor of the Egg class is called to create an instance of the Egg class and its nested Yolk class. Then, the constructor of the BigEgg class is called to create an instance of the BigEgg class and its overridden nested Yolk class.
Skotique
Уровень 35
14 октября 2023, 09:59
в вашем коде: 1) вызывается main() 2) выполняется команда new BigEgg() 3) вызывается конструктор BigEgg 4) в нем: - автоматически вызывается конструктор суперкласса Egg без параметров, печатается New Egg() - поле y класса Egg инициализируется созданием экземпляра внутреннего класса Egg.Yolk - вызывается конструктор класса Egg.Yolk, печатается Egg.Yolk() 4) выполнение возвращается в конструктор BigEgg - в нем новая переменная z инициализируется созданием экземпляра внутреннего класса BigEgg.Yolk - вызывается конструктор класса BigEgg.Yolk, печатается BigEgg.Yolk() в исходном коде автора примера из лекции не было переопределенного конструктора
public BigEgg() {
    Yolk z = new Yolk();
}
(который инициализировал бы переопределенный класс BigEgg.Yolk.)
Skotique
Уровень 35
14 октября 2023, 10:13
Заявление, что внутренний класс это поле, очень хорошее. (Если только считать его полем своего рода, к которому невозможно напрямую обратиться без экземпляра внешнего класса.) Переопределение внутреннего класса затенением это очень интересно.
Alexey Svorkin
Уровень 37
12 июня 2022, 07:56
Во что компилируются анонимные внутренние классы? Анонимные внутренние классы компилируются в файлы внешнийКласс$n.class. На месте внешнего класса, соответственно, название обрамляющего класса, внутри которого описывается анонимный внутренний класс. На месте n число от 1 до количества анонимных классов. Я не понял , что?!🤯
Hamlet simonyan
Уровень 34
27 сентября 2022, 20:11
типа появляется файл класа с названием по этому внешнийКласс$n.class шаблону
Саня
Уровень 38
8 августа 2021, 08:58
Можно ли объявлять внутренние классы private? Да, можно. PS Обоснования так и не нашел, но на философии java встречались подобные примеры. Плюс IDE не ругается. Буду признателен за обоснование, но предположу, что в этом плане внутренний класс ничем не отличается от обычного класса. Разница в наследовании: такое не скомпилируется из-за модификатора private у класса InnerA :
class A{
    private class InnerA{}
}
class B extends A{
    class InnerB extends InnerA{}
}
Igor Java/Kotlin Developer
17 июня 2021, 07:12
Круто, узнал что в Java есть множественное наследование. Но только в интерфейсах.🤦‍♂️
private interface MyInterface extends Runnable, WindowListener
Сергей
Уровень 38
26 сентября 2020, 16:31
Расскажите людям в конце концов, что для JVM нет разницы между внешними и внутренними классами, нет отдельного синтаксиса для внутреннего (вложенного) класса. Оба класса абсолютно равноценны, они не находятся один в другом. Компилятор автоматически видоизменяет эти классы, добавляя, например, в конструктор внутреннего класса параметр - ссылку на объект внешнего класса. А для доступа к private-полю внешнего класса добавляет во внешний класс статический метод типа access$0(OuterClass o), потом обращение к этому полю во внутреннем классе заменяет на вызов этого метода и т.д.
Anton Stezhkin
Уровень 41
13 февраля 2022, 12:08
Спасибо! Теперь все стало гораздо понятнее.
Nordis
Уровень 28
Expert
21 октября 2019, 04:34
Что это такое? - объемлющего.
llDmitry
Уровень 28
10 декабря 2019, 15:27
Содержащий в себе данный класс / внешний класс
Татьяна
Уровень 41
26 января 2020, 17:23
Я думаю речь идет об обрамляющем, т.е. внешнем классе
SERGEY
Уровень 31
13 ноября 2021, 14:08
Сразу видно, что уроки Хулио ты прогуливал
Victor
Уровень 35
9 октября 2018, 00:58
>Но! Начиная с Java8 мы можем обращаться в локальных классах к не финальным локальным >переменным, если они не были изменены до момента инициализации класса. Также теперь стало >возможным обращение к не финальным параметрам метода. не совсем верно, поля по сути остаются final (effective final), просто ключевое слово не обязательно добавлять.
Andrii Gorshunov
Уровень 41
Expert
22 января 2019, 21:47
Дополню. Если хотите менять переменную, например увеличивать или манипулировать (как со строками) тогда надо объявить как final, сделать копию и менять копию. Если только использовать переменную как "только чтение", те. без изменений, тогда их можно не объявлять как final и они будут считаться как effective final. (A variable or parameter whose value is never changed after it is initialized is effectively final.)
Nursultan
Уровень 31
6 июня 2020, 18:12
наверное, всё же, чтобы не путаться, лучше объявлять переменные как final=) а то можно и забыть про effective final))
lichMax
Уровень 40
19 апреля 2017, 14:35
По поводу восьмого вопроса: по-моему это довольно часто встречаемая практика. Просто создаётся класс для внутреннего пользования. Ну либо просто скрывается реализация этого класса, либо контролируется получение объектов этого класса, а также его тип (примеры была в «Философии Джава» Эккеля).
Slavaslav
Уровень 34
16 сентября 2016, 16:51
1. Во что компилируются анонимные внутренние классы?

9. Можно ли объявлять анонимные внутренние классы private?
lichMax
Уровень 40
19 апреля 2017, 14:24
Тоже был удивлён по поводу ответа на первый вопрос. Я для себя ответил по-другому.