image

Добрый день, Хабровчане! На собеседованиях мне довольно часто попадались вопросы про вложенные классы. Поэтому я решил разобраться в них, систематизировать свои знания, а заодно и поделиться этими знаниями с вами.

Вложенный класс (InnerClass)


public class OuterClass {
    public class InnerClass{
    }
}

Из него видны:
— все (даже private) свойства и методы OuterClassа обычные и статические.
— public и protected свойства и методы родителя OuterClassа обычные и статические. То есть те, которые видны в OuterClassе.

Его видно:
— согласно модификатору доступа.

Может наследовать:
— обычные классы.
— такие же внутренние классы в OuterClassе и его предках.

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

Может имплементировать интерфейс

Может содержать:
— только обычные свойства и методы (не статические).

Экзэмпляр этого класса создаётся так:
OuterClass outerClass = new OuterClass();
OuterClass.InnerClass innerClass = outerClass.new InnerClass();


Статический вложенный класс (StaticInnerClass)


public class OuterClass {
    public static class StaticInnerClass{
    }
}

Из него (самого класса) видны:
— статические свойства и методы OuterClassа (даже private).
— статические свойства и методы родителя OuterClassа public и protected. То есть те, которые видны в OuterClassе.

Из его экземпляра видны:
— все (даже private) свойства и методы OuterClassа обычные и статические.
— public и protected свойства и методы родителя OuterClassа обычные и статические. То есть те, которые видны в OuterClassе.

Его видно:
— согласно модификатору доступа.

Может наследовать:
— обычные классы.
— такие же статические внутренние классы в OuterClassе и его предках.

Может быть наследован:
— любым классом:
— вложенным
— не вложенным
— статическим
не статическим!

Может имплементировать интерфейс

Может содержать:
— статические свойства и методы.
— не статические свойства и методы.

Экзэмпляр этого класса создаётся так:
OuterClass.StaticInnerClass staticInnerClass = new OuterClass.StaticInnerClass();

Локальный класс (LocalClass)


public class OuterClass {
    public void someMethod(){
        class LocalClass{
        }
    }
}

Из него видны:
— все (даже private) свойства и методы OuterClassа обычные и статические.
— public и protected свойства и методы родителя OuterClassа обычные и статические. То есть те, которые видны в OuterClassе.

Его видно:
— только в том методе где он определён.

Может наследовать:
— обычные классы.
— внутренние классы в OuterClassе и его предках.
— такие же локальные классы определённые в том же методе.

Может быть наследован:
— таким же локальным классом определённом в том же методе.

Может имплементировать интерфейс

Может содержать:
— только обычные свойства и методы (не статические).

Анонимный класс (имени нет)


Локальный класс без имени. Наследует какой-то класс, или имплиментирует какой-то интерфейс.

public class OuterClass {
    public void someMethod(){
        Callable callable = new Callable() {
            @Override
            public Object call() throws Exception {
                return null;
            }
        };
    }
}

Из него видны:
— все (даже private) свойства и методы OuterClassа обычные и статические.
— public и protected свойства и методы родителя OuterClassа обычные и статические. То есть те, которые видны в OuterClassе.

Его видно:
— только в том методе где он определён.

Не может быть наследован

Может содержать:
— только обычные свойства и методы (не статические).

На этом всё. Жду ваших комментариев: какие есть неточности и ошибки, что я не покрыл и т.п.
Надеюсь, статья будет многим полезна.

Комментарии (13)


  1. NeoCode
    10.11.2017 15:00
    +1

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


    1. mayorovp
      10.11.2017 15:10

      Вы это все прочитали? Поздравляю :-)


      Разумеется, создать экземпляр вложенного класса — можно. Кстати, из такого класса видны любые члены внешнего класса, а не только статические: https://ideone.com/U3c9Qb, так что тут у автора полная ерунда написана...


      1. TheKnight
        10.11.2017 15:15

        Чутка не успел. Аналогичный случай. ideone.com/N1Czmt


        1. Andrey_139 Автор
          10.11.2017 15:54

          Уже исправил, Спасибо


          1. mayorovp
            10.11.2017 17:33

            А видимость нестатических членов внешнего класса почему не исправили?


            1. Andrey_139 Автор
              10.11.2017 17:51

              Только что проверил

              public class OuterClass {
                  public String outerStr = "outerStr";
                  public static class StaticInner{
                      public static void staticInnerMethod(){
                          System.out.println(outerStr);
                      }
                      public void innerMethod(){
                          System.out.println(outerStr);
                      }
                  }
              }

              Обе строки «System.out.println(outerStr);» выдают ошибку.
              Если сделать outerStr статическим — всё работает нормально


              1. aamonster
                10.11.2017 18:19
                +1

                Дык это не видимость, а вы не указали объект. Должно быть что-то вроде
                OuterClass oc;

                System.out.println(oc.outerStr);


                (я, если что, Java по сути не знаю, но ошибка не привязана к языку; простой способ понять — написать обращения к полям через this и подумать, что должно быть вместо него в данном случае)


  1. alex87is
    10.11.2017 15:12
    -1

    Хорошее резюме!!! Было бы супер, внизу сделать таблицу сравнения, чтобы разница в правилах(или ее отсутствие) была видна невооруженным глазом!


    1. cleaner_it
      11.11.2017 05:32

      Согласен, таблица будет вполне уместной. И примеры кода — как в комментариях


  1. Pro-invader
    10.11.2017 15:59
    +1

    Ещё не написали, что в одном файле исходного кода можно разместить множество не вложенных классов, не являющихся public, с уровнем доступа package-private (без модификатора доступа). В одном файле может содержаться только один public класс.
    После компиляции количество файлов будет равно количеству не вложенных классов.

    public class A {
    }
    class B {
    }
    class C {
    }


    1. ShinRa
      10.11.2017 17:31

      Я, например, стараюсь так не делать. Приходится сопровождать код с таким подходом, по 5-7 тысяч строк файлы, с множеством не вложенных классов. Это кошмар.


  1. Dimezis
    11.11.2017 00:48

    Стоит упомянуть, что если Inner класс обращается к приватным полям или методам внешнего класса, компилятор генерирует synthetic accessor метод с package модификатором видимости, чтобы тот в свою очередь мог достучаться до приватного метода.

    Таким образом, если вы вызывате приватный метод внешнего класса, вы на самом деле вызываете 2 метода, accessor и тот, к которому требуется доступ.

    Сделано это потому, что в Java после компиляции нет понятия Inner класса. Inner класс всего лишь станет package классом в том же пакете, рядом с внешним классом.


  1. vasiliy404alfertev
    13.11.2017 16:48

    Ещё у локальных классов есть доступ к локальным effectively final переменным метода, в котором он определён.