Какой предпочтительный синтаксис при использовании инъекции зависимостей на основе аннотаций в Котлин?

Мы используем существующую структуру, которая, среди прочего, обеспечивает инъекцию зависимостей через аннотации. В Java, чтобы ввести значение поля, мы сделали бы что-то вроде этого:

@Inject private SomeService someService; 

Поскольку отражение позволяет переназначить поля, которые обычно являются окончательными, это также действительное объявление:

 @Inject private final SomeService someService = null; 

Чтобы сделать объявление еще короче (а также сделать поля доступными в модульных тестовых классах, сохраняя их частью полу-частного API), вы можете сделать это следующим образом:

 @Inject SomeService someService; @Inject final SomeService someService; 

Когда дело доходит до Котлина, мы застряли в этом:

 @Inject private var someService: SomeService? = null @Inject private lateinit var someService: SomeService 
  • Подход нулевого типа требует дополнительной проверки нуля (или оператора !! ) для каждого использования службы, хотя мы на 100% уверены, что они никогда не будут нулевыми, если только что-то не пошло совершенно неправильно, и в этом случае у нас большие проблемы.
  • lateinit видимому, подход lateinit выполняет ненужные проверки во время выполнения (которые, к тому же, вряд ли станут узкими местами производительности, но все же) и довольно многословны.
  • Оба подхода позволяют переназначить переменную на другое значение. Нам еще предстоит найти способ замены var на val без переопределения getter или использования резервного поля, которое сталкивается с той же проблемой. По иронии судьбы, они во многих случаях длиннее Java-деклараций.

Итак, каков предпочтительный способ объявления аннотированных полей Kotlin, которые будут введены автоматически, и мы на 100% уверены, что они никогда не будут пустыми?

Хотя я понимаю, что существует несколько фреймворков с синтаксисом Kotlin, который разрешает подобные проблемы (например, Injekt ), я спрашиваю, есть ли более хороший способ сделать это с тем, что у нас есть.

Есть третий способ, который я использую в своих проектах. Но это взломать.

 fun <T> uninitialized(): T = null as T @Inject private val someService: SomeService = uninitialized() 

При использовании этого подхода вы можете использовать val и Kotlin не добавляет никаких null проверок.

Я лично хотел бы, чтобы у Котлина был законный способ добиться такого же поведения, но в настоящее время его нет. Некоторое время назад я создал проблему KT-10583 и описал, как проблема может быть решена. Но я не уверен, что это будет решено в ближайшем будущем.