← Назад к вопросам
В чем разница между inner class и nested class?
1.0 Junior🔥 181 комментариев
#Kotlin основы
Комментарии (1)
🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Разница между Inner Class и Nested Class в Kotlin
Это важное различие, которое часто путают. Оба — это классы, определённые внутри другого класса, но они работают совершенно по-разному.
Nested Class (静态 вложенный класс)
Nested Class — это статический вложенный класс. Он не имеет доступа к членам внешнего класса.
class Outer {
private val name = "Outer"
// Nested class (статический)
class Nested {
fun getData() {
// ❌ ОШИБКА! Не можем обратиться к name
// println(name)
}
}
}
// Создание Nested класса
val nested = Outer.Nested() // не нужен объект Outer!
Характеристики Nested:
- Не имеет неявной ссылки на Outer
- Создаётся без объекта внешнего класса
- Может быть private, protected, public
- Похож на обычный класс, просто находится в другом классе
- По умолчанию вложенные классы в Kotlin — Nested
Inner Class (внутренний класс)
Inner Class — это нестатический вложенный класс. Он имеет доступ к членам внешнего класса и содержит неявную ссылку на объект внешнего класса.
class Outer {
private val name = "Outer"
// Inner class (нестатический)
inner class Inner {
fun getData() {
// ✅ Можем обратиться к name
println(name)
// Также доступны private методы
privateMethod()
}
}
private fun privateMethod() {
println("Private method")
}
}
// Создание Inner класса
val outer = Outer()
val inner = outer.Inner() // НУЖЕН объект Outer!
inner.getData() // Выведет: Outer
Характеристики Inner:
- Имеет неявную ссылку на Outer
- Требует объект внешнего класса для создания
- Может быть private или protected (не public)
- Содержит скрытое поле
this@Outer - Требует ключевого слова
inner
Таблица сравнения
| Параметр | Nested Class | Inner Class |
|---|---|---|
| Ключевое слово | class | inner class |
| Ссылка на Outer | ❌ Нет | ✅ Да |
| Создание | Outer.Nested() | outer.Inner() |
| Доступ к членам Outer | ❌ Нет | ✅ Да |
| Потребление памяти | Меньше | Больше (на ссылку) |
| Memory leak риск | ❌ Низкий | ⚠️ Высокий |
| Статичность | Статичный | Динамический |
Практические примеры
Пример 1: Callback pattern
class MainActivity : AppCompatActivity() {
private val TAG = "MainActivity"
// ❌ ПЛОХО — Inner class может создать memory leak
inner class DownloadCallback {
fun onSuccess(data: String) {
Log.d(TAG, data) // доступен TAG
}
}
// ✅ ХОРОШО — Nested class, нет memory leak
class NetworkListener {
fun onConnected() {
// Не имеет доступа к Activity,
// но может получить его через параметр
}
}
}
Пример 2: Adapter для RecyclerView
class UserActivity : AppCompatActivity() {
private val viewModel by viewModels<UserViewModel>()
// ✅ ХОРОШО — Nested class
inner class UserAdapter(val items: List<User>)
: RecyclerView.Adapter<UserAdapter.UserViewHolder>() {
override fun onCreateViewHolder(
parent: ViewGroup,
viewType: Int
): UserViewHolder {
return UserViewHolder(
LayoutInflater.from(parent.context)
.inflate(R.layout.item_user, parent, false)
)
}
override fun onBindViewHolder(holder: UserViewHolder, position: Int) {
holder.bind(items[position])
}
override fun getItemCount() = items.size
inner class UserViewHolder(itemView: View)
: RecyclerView.ViewHolder(itemView) {
fun bind(user: User) {
itemView.textView.text = user.name
// Можем обратиться к viewModel
itemView.setOnClickListener {
viewModel.selectUser(user)
}
}
}
}
}
Пример 3: Builder pattern
class Person private constructor(
val name: String,
val age: Int,
val email: String
) {
// ✅ ХОРОШО — Nested class для Builder
class Builder {
private var name = ""
private var age = 0
private var email = ""
fun name(value: String) = apply { name = value }
fun age(value: Int) = apply { age = value }
fun email(value: String) = apply { email = value }
fun build() = Person(name, age, email)
}
}
// Использование
val person = Person.Builder()
.name("Alice")
.age(30)
.email("alice@example.com")
.build()
Memory Leak с Inner Class
Опасность Inner Class — может создать memory leak:
class Activity : AppCompatActivity() {
private val items = MutableList(1000) { "Item $it" }
// ❌ ПЛОХО — Inner class содержит скрытую ссылку на Activity
inner class DataLoader {
fun load() {
// Имеет доступ к items
println(items.size)
}
}
fun startLoad() {
// Если долгая операция, Activity не будет удалена из памяти
val loader = DataLoader()
// Loader содержит this (Activity) → Activity не может быть GC
}
}
// Правильный способ:
class Activity : AppCompatActivity() {
// ✅ Nested class + weakReference
class DataLoader(val items: List<String>) {
fun load() {
println(items.size)
}
}
fun startLoad() {
val loader = DataLoader(listOf("Item 1", "Item 2"))
// Loader не содержит ссылку на Activity → нет leak
}
}
Правило выбора
Нужен доступ к членам внешнего класса?
│
├─ ДА → Inner Class
│ ⚠️ Но будь осторожен с memory leak
│
└─ НЕТ → Nested Class
✅ Безопаснее и дешевле по памяти
Вывод
- Nested Class — статический вложенный класс, без ссылки на Outer
- Inner Class — динамический вложенный класс, с ссылкой на Outer
- По умолчанию используй Nested (безопаснее и эффективнее)
- Inner используй только если нужен доступ к Outer членам
- Осторожно с Inner в долгоживущих объектах (риск memory leak)