Статьи Kotlin. Вложенные и внутренние классы
Post
Cancel

Kotlin. Вложенные и внутренние классы

В Kotlin можно объявить один класс внутри другого. Как правило это полезно, когда нужно расширить функционал внешнего класса: будет чёткое разделение прямого предназначения класса от новой функциональности, при этом код будет рядом с местом использования. Подобные классы разделяются на вложенные (nested) и внутренние (inner).

Вложенные классы (Nested classes)

Для определения вложенного класса достаточно заключить его в фигурные скобки внутри внешнего класса. Не нужно указывать какие-либо ключевые слова, потому что вложенные классы - это значение по умолчанию.

1
2
3
4
5
6
7
class Outer {       // внешний класс
  ...

  class Nested {    // вложенный класс
    ...
  }
}

У вложенных классов есть два ограничения:

  • Экземпляр внешнего класса не может напрямую обращаться к вложенному классу. Вложенный класс должен быть инициализирован внутри внешнего класса.
1
2
3
4
5
6
7
8
9
10
11
class Outer {
  ...
  class Nested {
    ...
  }
}

---

val nested = Outer.Nested()    // скомпилируется
val nested = Outer().Nested()  // не скомпилируется
  • Вложенный класс не имеет доступа к экземпляру внешнего класса, поэтому не сможет обращаться к его компонентам.
1
2
3
4
5
6
7
class Outer {
  val x = 10

  class Nested {
    fun getX() = "x = $x"  // не скомпилируется
  }
}

Внутренние классы (Inner classes)

Для определения внутреннего класса нужно не только заключить его в фигурные скобки внутри внешнего класса, но и отметить ключевым словом inner.

1
2
3
4
5
6
7
class Outer {              // внешний класс
  ...

  inner class Inner {      // внутренний класс
    ...
  }
}

По сути внутренний класс - это вложенный класс, который может обращаться к компонентам внешнего класса. В этом и есть их ключевое отличие.

1
2
3
4
5
6
7
class Outer {
  val x = 10

  inner class Inner {
    fun getX() = "x = $x"  // скомпилируется
  }
}

Что если имена переменных и функций внутреннего класса совпадают с именами внешнего класса? В этом случае при обращении к компонентам внешнего класса используется ключевое слово this.

1
2
3
4
5
6
7
8
class Outer {
  val x = 10

  inner class Inner {
    val x = 20
    fun getX() = "Outer x = ${this@Outer.x}, Inner x = $x"
  }
}

Второе ключевое отличие заключается в том, как происходит обращение к внутреннему классу. Экземпляр внутреннего класса всегда хранит ссылку на экземпляр внешнего класса. Поэтому для обращения к нему (откуда угодно) нужно сначала создать экземпляр внешнего класса, а затем на его основе экземпляр внутреннего класса.

1
2
val inner = Outer().Inner() // скомпилируется
val inner = Outer.Inner()   // не скомпилируется

Сравнения с Java

Сами по себе понятия вложенных и внутренних классов в Java ничем не отличаются от Kotlin. Отличается только то, как они объявляются. Поэтому я думаю, что таблички в качестве сравнения будет достаточно.

Класс, объявленный внутри другого классаJavaKotlin
Вложенный классstatic class Bclass B
Внутренний классclass Binner class B

Полезные ссылки

Nested and Inner Classes - официальная документация.
Вложенные классы - перевод на русский.

This post is licensed under CC BY 4.0 by the author.

Kotlin. Классы данных (Data classes)

Kotlin. Изолированные (запечатанные) классы (sealed classes).