Kotlin генерирует конструктор, который устанавливает значения по умолчанию для нулевых аргументов

Возьмем класс класса данных:

data class User( val userNumber: Int = -1, val name: String, val userGroups; List<String> = emptyList(), val screenName: String = "new-user" ) 

При вызове этой функции из Котлина это довольно просто. Для этого я могу просто использовать синтаксис named-argument. Вызов из Java, я должен указать все значения или использовать аннотацию @JvmOverloads , которая генерирует следующие конструкторы (в дополнение к конструктору, который kotlin генерирует с битовой маской для значений по умолчанию):

 User(int userNumber, @NotNull String name, @NotNull List userGroups, @NotNull String screenName) User(int userNumber, @NotNull String name, @NotNull List userGroups) User(int userNumber, @NotNull String name) User(@NotNull String name) 

Теперь, если я хочу создать объект User в Java, эквивалентный User(name="John Doe", userGroups=listOf("admin", "super") я не могу сделать это с помощью указанных выше конструкторов. это, если я положил val userNumber: Int = -1 в конце в объявлении data class (генерация конструкторов, похоже, зависит от порядка, в котором определяются необязательные аргументы). Это хорошо, потому что ожидая, что kotlin будет генерировать все перестановки сильно раздувая некоторые классы.

Самая большая проблема в том, что такие инструменты, как Jackson просто не работают, поскольку они не знают, какой конструктор использовать (а не как я могу аннотировать один из сгенерированных специально).

Итак, есть ли способ генерации (одного) конструктора, например:

 User(Integer userNumber, String name, List<String> userGroups, String screenName) { this.userNumber = (userNumber == null) ? -1 : userNumber; this.userGroups = (userGroups == null) ? Collections.emptyList() : userGroups; //... } 

В настоящее время я использую вышеуказанный подход, но вручную определяю конструкторы, в которых они мне нужны.

РЕДАКТИРОВАТЬ

Я должен пояснить, что создание подобного конструктора не работает, очевидно, потому что обе подписи будут сталкиваться с JVM. Это то, что он хотел бы в моем случае:

 data class User( val userNumber: Int = -1, val name: String, val userGroups; List<String> = emptyList(), val screenName: String = "new-user" ) { companion object { @JvmStatic @JsonCreator fun constructionSupport( @JsonProperty("userNumber") userNumber : Int?, @JsonProperty("name") name : String, @JsonProperty("userGroups") userGroups : List<String>?, @JsonProperty("screenName") screenName : String? ) = User( userNumber = userNumber ?: -1, name = name, userGroups = userGroups ?: emptyList(), screenName = screenName ?: "new-user" ) } } 

Также обратите внимание на избыточность, когда мне нужно дважды писать значения по умолчанию для свойств. Теперь, когда я смотрю на это, я сомневаюсь, что для этого существует решение. Может быть, это хороший вариант использования для kapt основанного на kapt

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

 fun build_user(userNumber: Int?, name: String, userGroups: List<String>?, screenName: String?) : User { return User(if(userNumber !== null) userNumber else -1, name, if(userGroups !== null) userGroups else emptyList(), if(screenName !== null) screenName else "new-user") } 

Затем, когда вам это нужно, вы просто вызываете это из Java:

 User user = UserKt.build_user(null, "Hello", null, "Porterhouse Steak"); System.out.println(user); 

Вывод из примера:

 User(userNumber=-1, name=Hello, userGroups=[], screenName=Porterhouse Steak) 

Метод находится где-то между конструктором и строителем. Он бьет, выбирая полномасштабный объект Builder , и избегает загромождения вашего data class ненужной беспорядочной связью кода Java-interop.

Дополнительные сведения см. В разделе Функции уровня пакета .

Лучшее решение – добавить библиотеку для понимания функциональности Kotlin. Например, для Джексона существует jackson-module-kotlin . С помощью этой библиотеки мы можем использовать аргументы по умолчанию в классах данных.

Пример:

 data class User( val userNumber: Int = -1, val name: String, val userGroups: List<String> = emptyList(), val screenName: String = "new-user" ) fun main(args: Array<String>) { val objectMapper = ObjectMapper() .registerModule(KotlinModule()) val testUser = User(userNumber = 5, name = "someName") val stringUser = objectMapper.writeValueAsString(testUser) println(stringUser) val parsedUser = objectMapper.readValue<User>(stringUser) println(parsedUser) assert(testUser == parsedUser) { println("something goes wrong") } } 
Intereting Posts
Использование цветов, определенных в базовом модуле из функционального модуля, не выполняется после восстановления Android Instant App Сохранять двойные кавычки при получении элемента из JSONArray Ошибка «не должна быть пустой» в Kotlin Импортировать вложенные объекты / функции объекта в Котлин Котлин: Нужно ли защищать класс данных Котлина? Использование функциональных интерфейсов с функциями функций в Котлине Не иметь доступа к контроллеру Spring boot, но иметь доступ к index.html Библиотека «Столкновение с пространством» вылетает со смертельным исходом 11 (SIGEGV) при вставке и удалении Как перебирать иерархию классов? Метод Kotlin inline не видим, если не расширять класс Как реализовать clicklistener в элементе customadpter в kotlin? Ручка ACTION_UP в OnLongPress Android Как указать поле в коробке может быть равно NULL в моем коде миграции Realm? Преобразование массива ArrayMap в ArrayList – Kotlin Изображение не отображается в Recyclerview с помощью Glide