@PublishedApi: Как открыть internal и не покраснетьВ прошлом посте мы говорили, что inline функции нарушают инкапсуляцию. Но что делать, если вам реально нужно обратиться к
internal свойству внутри инлайн-функции?Компилятор ударит по рукам:
"Public-API inline function cannot access non-public declarations"И он прав. Поскольку код инлайнится в модуль клиента, он физически не сможет вызвать ваш
internal метод, так как байт-код клиента ничего не знает о ваших внутренних секретах.Тут на сцену выходит
@PublishedApi.Смотрим код:
class FastCache {
// Обычный internal - доступен только внутри модуля
internal val backingMap = hashMapOf<String, Any>()
// ❌ ОШИБКА КОМПИЛЯЦИИ:
// Inline функция видна всем, а backingMap - нет.
inline fun getOrPut(key: String, block: () -> Any): Any {
return backingMap.getOrPut(key, block)
}
}
Чтобы это заработало, мы должны сказать компилятору: "Я понимаю риски, сделай это поле публичным в байт-коде, но скрой его в IDE от чужих глаз".
Как надо:
class FastCache {
@PublishedApi // 👈 Магия здесь
internal val backingMap = hashMapOf<String, Any>()
inline fun getOrPut(key: String, block: () -> Any): Any {
return backingMap.getOrPut(key, block) // ✅ Работает!
}
}
Что происходит Under the Hood?
1. В Source Code: Поле остается
internal. Если кто-то попробует написать fastCache.backingMap из другого модуля - IDE не подскажет, а компилятор выдаст ошибку.2. В Bytecode: Поле становится
public (или генерируются публичные акцессоры). Это нужно, чтобы заинлайненный код на стороне клиента мог технически выполнить вызов.⚠️ Warning для архитекторов:
Используя
@PublishedApi, вы подписываете кровью контракт о бинарной совместимости.Если вы в следующей версии библиотеки переименуете или удалите
backingMap, код всех клиентов, кто заинлайнил вашу функцию getOrPut, упадет с NoSuchMethodError или NoSuchFieldError в рантайме (даже без перекомпиляции).Вердикт:
Инструмент мощный для создания zero-overhead оберток, но требует дисциплины. Меняете
@PublishedApi поле? Поднимайте мажорную версию библиотеки.Кто пишет свои либы, часто приходится так "оголять" кишки классов?
✍️ @kotlin_lib