← Назад к вопросам

Какие модификаторы доступа существуют в Kotlin? Чем internal отличается от protected?

2.3 Middle🔥 111 комментариев
#Kotlin основы#Многомодульность

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

🐱
claude-haiku-4.5PrepBro AI26 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Модификаторы доступа в Kotlin: public, private, protected, internal

Котлин имеет четыре уровня видимости (visibility modifiers), которые контролируют, где можно использовать класс, функцию или переменную. Это отличается от Java, где есть дополнительные нюансы с package-private.

Уровни видимости в Kotlin

1. public (по умолчанию) Доступно везде — в любом пакете, модуле, проекте.

public class User // или просто: class User
public fun getName() {}

2. private Доступно только внутри файла или класса.

private class Helper // видно только в этом файле
class User {
    private val id: String = "" // видно только в этом классе
    private fun validate() {} // видно только в этом классе
}

3. protected Доступно внутри класса и его наследников.

open class BaseActivity {
    protected fun onCreate() {} // видно в наследниках
}

class MainActivity : BaseActivity() {
    override fun onCreate() { // OK — наследник может переопределить
        super.onCreate()
    }
}

// ❌ Ошибка — protected не видно снаружи
val activity = MainActivity()
activity.onCreate() // Compilation error

4. internal (уникально для Kotlin) Доступно только внутри одного модуля (module scope). Модуль — это набор Kotlin файлов, скомпилированных вместе.

internal class InternalHelper // видно только в этом модуле
internal fun internalFunction() {} // видно только в этом модуле

protected vs internal — ключевое различие

// Module 1 (app:core)
open class BaseService {
    protected fun protectedMethod() {} // Видно только наследникам
    internal fun internalMethod() {} // Видно в модуле core
}

// Module 2 (app:feature)
class FeatureService : BaseService() {
    fun use() {
        protectedMethod() // ✅ OK — это наследник BaseService
        internalMethod() // ❌ ERROR — internal только для модуля core
    }
}

// Внешний код
val service = BaseService()
service.protectedMethod() // ❌ ERROR — protected требует наследования
service.internalMethod() // ❌ ERROR — internal только в модуле core

Таблица видимости

МодификаторКлассНаследник (тот же модуль)Наследник (другой модуль)Другой пакет (тот же модуль)Другой модуль
public
internal
protected
private

Практические примеры

protected — для наследования:

abstract class Animal {
    protected open fun makeSound() {} // Переопределить в наследниках
}

class Dog : Animal() {
    override fun makeSound() { // OK
        println("Woof")
    }
}

val dog = Dog()
dog.makeSound() // ❌ ERROR — protected

internal — для модулей:

// app:core module
internal class CoreHelper {
    internal fun doSomething() {}
}

internal val API_KEY = "secret"

// app:ui module (другой модуль)
class UIComponent {
    fun use() {
        CoreHelper() // ❌ ERROR — internal
        API_KEY // ❌ ERROR — internal
    }
}

// Решение:
// app:core module
public interface CoreService { // Публичный интерфейс
    fun doSomething()
}

internal class CoreServiceImpl : CoreService {
    override fun doSomething() {}
}

public fun createCoreService(): CoreService {
    return CoreServiceImpl() // Возвращаем интерфейс, скрывая имплементацию
}

private — для деталей реализации:

class Calculator {
    fun add(a: Int, b: Int): Int {
        validate(a, b) // Приватная вспомогательная функция
        return a + b
    }
    
    private fun validate(a: Int, b: Int) { // Деталь реализации
        if (a < 0 || b < 0) throw IllegalArgumentException()
    }
}

Правила использования

Используй правило минимальной видимости:

  • По умолчанию делай всё private
  • Расширяй видимость только если нужно
  • Для API используй public интерфейсы, скрывай имплементацию через internal
// ❌ Плохо — всё public
public class UserManager {
    public val users = mutableListOf<User>()
    public fun loadUsersFromDatabase() {}
}

// ✅ Хорошо — скрыта реализация
public interface UserManager {
    fun getUsers(): List<User>
}

internal class UserManagerImpl : UserManager {
    private val users = mutableListOf<User>()
    private fun loadUsersFromDatabase() {}
    override fun getUsers() = users
}

Вывод

protected — для наследования (только наследники видят), internal — для модулей (весь модуль видит, но другие модули не видят). Используй их правильно, чтобы скрывать детали реализации и создавать чистые API.