JAVA

Тонкости и всякие полезности
На главную

JSON

Валидатор JSON

Аннотации по JSON и то же, но на русском


Полезные аннотации

@JsonPropertyOrder({"id", name"})

Для классов порядок сериализации полей объекта в JSON

@JsonInclude(JsonInclude. Include .NON_NULL)

не включать параметр если он равен null, можно для всего класса или для конкретного поля

@JsonProperty("<имя_в_json>")

Аннотация ставится над переменной и в json вместо имени переменной попадет указанное в аннотации значение


JsonInclude атрибуты

ALWAYS всегда включать свойство
NON_NULL не включать свойство с нулевым значением
NON_ABSENT нет нулевых значений, включая нулевые значения содержимого, такие как Optional, AtomicReference и т. д.
NON_EMPTY значения вроде пустых коллекций/карт/массивов/строк и т. д. исключаются (NON_NULL + NON_ABSENT)
NON_DEFAULT без значений по умолчанию, например нет примитивов со значениями по умолчанию
CUSTOM настраиваемый фильтр для исключения, заданный JsonInclude.valueFilter()
USE_DEFAULTS использовать значения по умолчанию либо из уровня класса, либо из уровня ObjectMapper

Optional


Optional.of возвращает Optional-объект
Optional.ofNullable возвращает Optional-объект, а если нет дженерик-объекта, возвращает пустой Optional-объект
Optional.empty возвращает пустой Optional-объект

Почитать

Optional: Кот Шрёдингера в Java 8

Java 8 Optional


РАБОТА С OPTIONAL

Обычно с Optional работают так:

Это ок, но встроенные методы Optional позволяют обойтись без связки isPresent + get.

  1. Значение по умолчанию: orElse / orElseGet

    Вместо сложной конструкции

    NO: if (opt.isPresent()) { return opt.get(); } else return "default";

    используйте метод orElse:

    YES: return opt.orElse("default");

    ВАЖНО!!!

    Используйте orElse только с константами или простыми операциями, потому что выражение в orElse вычисляется всегда:

    BAD: getFromCache(id) .orElse(getFromDB(id);

    getFromDB выполнится, даже если getFromCache вернёт результат.

    Для тяжёлых операций используйте метод orElseGet:

    GOOD: getFromCache(id) .orElseGet(() → getFromDB(id));

    Здесь getFromDB выполнится, только если результат getFromCache пустой.

  2. Бросить эксепшн, если результата нет: orElseThrow

    return opt.orElseThrow(NotFoundException::new)

    В Java 10 появился метод без параметров: opt.orElseThrow()

    Если в opt нет значения, код выбросит NoSuchElementException.

  3. Проверка условия: filter

    Вместо isPresent + get + проверка условия используйте метод filter:

    value.filter(v→v.length()<4) .ifPresent(…)

  4. Преобразования: map / flatMap

    Для манипуляций значением Optional используйте метод map:

    opt.map(Account::getAddress) .map(String::toUpperCase) .orElse("default");

    flatMap используется, если метод отображения возвращает Optional:

    Optional<String> getAddress() {…}

    opt.flatMap(Account::getAddress);

  5. Методы для Stream API (Java 9)

    Когда в стриме один метод возвращает Optional, его часто обрабатывают так:

    BAD: .filter(Optional::isPresent) .map(Optional::get)

    Метод flatMap пригодится и здесь. Все пустые значения отфильтруются:

    GOOD: .flatMap(Optional::stream)

  6. Сравнение Optional

    BAD: Обычно делают так: op1.get().equals(op2.get())

    Если хотя бы один Optional пустой, будет NullPointerException

    GOOD: Сравнивайте Optional напрямую: op1.equals(op2)


5 ошибок при использовании Optional

Optional — удобный инструмент, который появился в java 8. В документации его цель явно обозначена — возвращаемое значение из метода, когда результата может не быть.

Например, функция поиска может найти нужный элемент, а может и не найти. В этой ситуации отлично подойдёт Optional:

Optional <String> search();

Самые частые ошибки при использовании:

  1. Входные параметры Optional в методах и конструкторах

    Класс становится неудобным, приходится добавлять лишние обёртки и подгонять параметры.

    BAD: void init(Optional<String> value) {…}

    GOOD: Проверяйте параметры на null в начале метода:

    void init (String value) {

    if (value == null) …

    }

  2. Усложнение кода

    С появлением Stream API усилился тренд на Fluent API: это когда действия соединяются в одну цепочку. Не создавайте Optional только ради этого, пишите простой код.

    BAD: return Optional.ofNullable(value) .orElse("default");

    GOOD: return value == null ? "default" : value;

  3. Отложенная обработка Optional

    Идеально, если обработка происходит сразу после возвращения из метода:

    Optional <String> valueOpt = … ;

    String value = valueOpt.orElse("default");

  4. Optional с коллекциями

    Возвращайте пустой список, если элементов нет:

    BAD: public Optional <List<Integer>> search(…)

    GOOD: public List <Integer> search(…)

  5. Работа с примитивами

    Для примитивов int, long и double используйте специальные Optional классы:

    - OptionalInt

    - OptionalDouble

    - OptionalLong

    Нет затрат на создание объекта, а код становится чуть короче и понятнее. Методов меньше, чем в Optional, но при интенсивной работе с примитивами возможен прирост производительности.

Почему нельзя возвращать NULL?

Смотрите видосик (тут только слова)

А также смотрите как с этим бороться (Null safety)

Базовые кейсы

на анализ кода

instanceof

Вопрос: Что будет выведено в консоль?

public class Loader {

public static void main(String[] args) {

B b = new C();

A a = b;

if (a instanceof A) System.out.println("A");

if (a instanceof B) System.out.println("B");

if (a instanceof C) System.out.println("C");

if (a instanceof D) System.out.println("D");

}

}

class A {}

class B extends A {}

class C extends B {}

class D extends C {}

Ответ: A B C

Если непонятно, то читайте про instanceof.


Перегрузка методов (method overloading)

Вопрос: Что будет выведено в консоль?

class SuperBase {

public int i = 3;

public void foo(Object o) {

System.out.println("Object " + i);

}

public void foo(String s) {

System.out.println("String " + i);

}

}

class Base extends SuperBase {

public Base() {

i = 5;

}

public static void main(String[] args) {

SuperBase sb = new Base();

Object o = "";

sb.foo(o);

sb.foo("");

}

}

Ответ: Object 5 String 5


Inheritance (наследование)

Вопрос: Каким будет результат кода?

class Parent {}

class DeriveOne extends Parent {}

class DeriveTwo extends Parent {}

Parent p = new Parent();

DeriveOne d1 = new DeriveOne();

DeriveTwo d2 = new DeriveTwo();

d1 = (DeriveOne) d2;

Ответ: Ошибка компиляции.

Компилятору сразу понятно, что невозможно кастировать d2 к классу DeriveOne, т.к. d2 не является наследником DeriveOne

Интересные вопросы

С разных собесов

Может ли null быть ключом в Map?

Можно ли запустить Spring без Hibernate и наоборот?

Как между собой могут связываться микросервисы?

Почему Spring Boot быстрее Spring'а?