Какие модификаторы доступа существуют в Kotlin? Чем internal отличается от protected?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Модификаторы доступа в 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.