Kotlin: дочерний конструктор, передающий значение по умолчанию родительскому

#java #kotlin

#java #kotlin

Вопрос:

Рассмотрим следующие классы:

 open class Parent(foo: Map<a, b>)

class Child(foo: Map<a, b>): Parent(foo)
  

Похоже, здесь это работает, потому что определения foo идентичны в обоих классах. Однако, когда я пытаюсь сделать

 class Child(foo: Map<a, b>? = mapOf()): Parent(foo)
  

Это больше не работает, потому что foo определено как необязательное в Child классе.
Это вызывает у меня проблемы, потому что я пытаюсь инициализировать Child класс в Java, и, очевидно, это не позволило бы мне выборочно передавать аргументы, такие как Kotlin, поэтому мне было интересно, что я мог бы сделать здесь, чтобы позволить вызывающему Java передавать null в foo поле, чтобы поле автоматически инициализировалось на пустой карте.

Спасибо

РЕДАКТИРОВАТЬ: Также был бы признателен, если бы кто-нибудь мог объяснить, почему Kotlin не пытался передать foo значение по умолчанию в Child в Parent ? Мы все знаем, что foo in Child в любом случае определенно не будет полным.

Комментарии:

1. Мы этого не знаем! С помощью этой подписи я могу легко создать Child(foo = null) , и значение по умолчанию не будет применяться!

Ответ №1:

Лучшим вариантом для клиента дочернего класса было бы не передавать какой-либо параметр вместо null, поэтому вы можете просто указать значение по умолчанию для foo map в дочернем конструкторе

 class Child(foo: Map<String, String> = emptyMap()): Parent(foo)
  

затем компилятор kotlin сгенерирует два конструктора дочернего класса, чтобы вы могли использовать его по своему усмотрению в Java

 Child child = new Child();
Child secondChild = new Child(new HashMap<>());
  

Комментарии:

1. Похоже, это работает хорошо, если речь идет только об одном необязательном параметре. Однако кажется, что количество вторичных конструкторов увеличится, когда мы добавим больше опций?

2. да, но если у вас есть больше параметров, то, возможно, вам вообще не нужен этот null, вы можете указать только значение параметра foo по умолчанию в дочернем классе, и компилятор kotlin сгенерирует для вас два конструктора для использования, я обновлю свой ответ

3. Мне любопытно, что произойдет, если кто-то передаст HashMap значение которого равно null? Скажите HashMap<> myMap = null .

4. вы получите исключение IllegalArgumentException во время выполнения вашей Java-программы, компилятор kotlin создаст конструктор, который выглядит следующим образом (это декомпилированная версия из байт-кода) public Child(@NotNull Map foo) { Intrinsics.checkParameterIsNotNull(foo, "foo"); super(foo); }

Ответ №2:

чтобы позволить вызывающей стороне Java передавать значение null в поле foo, чтобы поле автоматически инициализировалось для пустой карты

Если вы передадите null (в Java или Kotlin, не имеет значения), он будет инициализирован null . Вы можете сделать что-то вроде

 class Child(foo: Map<a, b>? = null): Parent(foo ?: mapOf())
  

вместо этого. Или используйте @JvmOverloads :

 class Child @JvmOverloads constructor(foo: Map<a, b> = mapOf()): Parent(foo)
  

для создания перегрузки без параметров. На самом деле, это особый случай, когда в этом нет необходимости:

Обратите внимание, что, как описано во вторичных конструкторах, если класс имеет значения по умолчанию для всех параметров конструктора, для него будет сгенерирован общедоступный конструктор без аргументов. Это работает, даже если @JvmOverloads аннотация не указана.