Как создать конструктор по умолчанию для неизменяемого класса - PullRequest
0 голосов
/ 21 сентября 2018

Мне нравится делать мои объекты неизменяемыми на основе этой статьи (Почему объекты должны быть неизменными) .

Однако я пытаюсь проанализировать объект с помощью Jackson Object Mapper.Первоначально я получал JsonMappingException: No suitable constructor found for type [simple type, class ]: cannot instantiate from JSON object.

Я мог исправить это, как упомянуто здесь , предоставив конструктор по умолчанию и сделав мои поля не окончательными.

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.NonNull;

@AllArgsConstructor
// @NoArgsConstructor(access = AccessLevel.PRIVATE)
@Builder
@Data
public class School {

    @NonNull
    private final String schoolId;

    @NonNull
    private final String schoolName;
}

Чтохороший стиль программирования, которому я должен следовать, чтобы преодолеть эту проблему?Есть ли единственный способ сделать мои объекты изменчивыми?

Можно ли использовать другой преобразователь, который не использует конструктор по умолчанию?

Ответы [ 2 ]

0 голосов
/ 26 сентября 2018

Существует гораздо лучшая альтернатива статическому фабричному методу, аннотированному @JsonCreator, в котором есть конструктор для всех элементов (как в любом случае требуется для неизменяемых классов).Аннотируйте , с помощью @JsonCreator, а также аннотируйте все параметры с помощью @JsonProperty следующим образом:

class School {
    //fields

    @JsonCreator
    public School(
            @JsonProperty("id") String id,
            @JsonProperty("name") String name) {
        this.schoolId = id;
        this.schoolName = name;
    }

    //getters
}

Это параметры, которые дает вам аннотация @JsonCreator.Он описывает их так в своей документации:

  • Метод конструктора / фабрики с одним аргументом без аннотации JsonProperty для аргумента: если это так, это так называемый «создатель делегата», в которомcase Джексон сначала связывает JSON с типом аргумента, а затем вызывает создателя.Это часто используется вместе с JsonValue (используется для сериализации).
  • Метод конструктора / фабрики, где каждый аргумент аннотируется с помощью JsonProperty или JacksonInject, чтобы указать имя свойства для привязки к

При некоторых обстоятельствах вам даже может не потребоваться явно указывать имя параметра.Документация относительно этого для @JsonCreator далее гласит:

Также обратите внимание, что все аннотации JsonProperty должны указывать фактическое имя (НЕ пустая строка для «по умолчанию»), если вы не используете один из модулей расширения, которые могут обнаружить параметрназвание;это потому, что версии JDK по умолчанию до 8 не смогли сохранить и / или получить имена параметров из байт-кода.Но с JDK 8 (или с использованием вспомогательных библиотек, таких как Paranamer, или других языков JVM, таких как Scala или Kotlin), указывать имя необязательно.

В качестве альтернативы это также будет хорошо работать с версией lombok 1.18.3 иливверх, где вы можете добавить lombok.copyableAnnotations += com.fasterxml.jackson.annotation.JsonProperty к вашему lombok.config и, следовательно, сделать так, чтобы он скопировал аннотации JsonProperty в конструктор, учитывая, что вы аннотируете все поля с ним (что в любом случае следует делать imo).Чтобы поместить аннотацию @JsonCreator в конструктор, вы можете использовать экспериментальную функцию onX .Используя @Value от lombok для неизменных классов данных, ваш DTO может выглядеть так (не проверено):

@Value
@AllArgsConstructor(onConstructor = @__(@JsonCreator))
class School {
    @JsonProperty("schoolId")
    String schoolId;
    @JsonProperty("schoolName")
    String schoolName;
}
0 голосов
/ 21 сентября 2018

Вы можете использовать фабрику Джексона (метод, помеченный @ JsonCreator ), который считывает поля с карты и вызывает ваш конструктор не по умолчанию:

class School {
    //fields

    public School(String id, String name) {
        this.schoolId = id;
        this.schoolName = name;
    }

    @JsonCreator
    public static School create(Map<String, Object> object) {
        return new School((String) object.get("schoolId"), 
                          (String) object.get("schoolName"));
    }

    //getters
}

Джексон вызовет create метод с Map версией json.И это эффективно решает проблему.

Я полагаю, что ваш вопрос ищет решение Джексона, а не новый шаблон / стиль.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...