В очередной раз читаю спор а-ля “динамическая типизация против статической”. Заметил вот что.

Попадаются отчаянные джависты, которые говорят: даже простые значения нужно делать классами, чтобы не допустить ошибки. Например, телефон должен быть не строкой, а классом Phone, который оборачивает строку; почта — классом Email, имя — Name и так далее.

Идея в том, что если метод принимает имя, телефон и почту, то строки легко перепутать: передать почту вместо имени или телефон вместо почты. А если параметры типизированы — Name, Email, Phone, — то ошибки быть не может.

То же самое предлагается делать с числами: температура по Фаренгейту — один класс, по Цельсию — другой. Для коров и лошадей тоже разные классы: HorseNumber и CowNumber. У таких классов свои методы сложения и вычитания, чтобы можно было сложить коров только с коровами, но не дай бог с лошадьми.

К счастью, в Джаве нельзя наследоваться от базовых классов вроде String, Number и других. Каким-то образом ее создатели догадались, что если каждый Васян будет делать свои строки и числа, то откроется дверь в ад.

Но нашлись лазейки. Иные джависты делают записи с одним полем value, примерно так:

public record Phone(value String) {};

Phone phone = new Phone("223-322");

Примеры, что они приводят, на первый взгляд красивы. Сигнатура как в примере ниже

someMethod(Name name, Email email, Phone phone)

действительно выглядит лучше, чем String, String, String.

Однако первая беда в том, что ничто не мешает мне выполнить такой код:

Phone phone = new Phone("test@hello.com");

и передать этот псевдо-телефон в метод. Можно добавить валидацию в конструктор, но в любом случае она сработает в рантайме — компилятор ничего не знает о том, что строка test@hello.com — это неправильный телефон.

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

UserPhone ph = new UserPhone(phone.value());

Джавистам, которые мечтают о таком подходе, я бы пожелал столкнуться с ним на практике. Чтобы у них был проект с классами ЧислоЯблок, ЯблокВЯщике, ЧислоЯщиков, ВозрастПользователя, ВозрастСотрудника, КуритИлиНет, МужчинаИлиЖенщина и так далее. Как говорится, бойся своих желаний!

Кстати, чтобы избежать путаницы в параметрах, они должны поддерживать передачу по имени, например так:

new User(name="...", phone="...", email="...");

В Джаве такого нет; там приняты билдеры:

User.builder().name("...").phone("...").email("...").build();

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

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