paint-brush
Как исправить проблемы Kotln при работе с целями использования сайтак@jesperancinha
106 чтения

Как исправить проблемы Kotln при работе с целями использования сайта

к João Esperancinha10m2024/03/21
Read on Terminal Reader
Read this story w/o Javascript

Слишком долго; Читать

Почему и где нам следует использовать цели use-site в Kotlin и чем это отличается от Kotlin при использовании таких фреймворков, как Spring Framework.
featured image - Как исправить проблемы Kotln при работе с целями использования сайта
João Esperancinha HackerNoon profile picture
0-item
1-item
2-item
3-item


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


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


Все сводится к неизменности или, во всяком случае, к ее возможности. Давайте вернемся в 1995 год, когда в какой-то момент мы начали создавать структуры данных, в которых мы могли бы указывать разные вещи. Мы определим поля, геттеры, сеттеры, свойства, средства доступа, атрибуты и параметры таким образом, чтобы иметь возможность передавать наши аргументы методам, функциям или конструкторам. Для этой части у нас будет немного забавной программы с использованием персонажей сериала середины 80-х: «Золотые девушки». Сказав это, давайте посмотрим, как будет выглядеть класс через 15 лет:


 public class GoldenGirlsJava { public String goldenGirl1; private final String goldenGirl2; private final String goldenGirl3; private final String goldenGirl4; public GoldenGirlsJava() { this.goldenGirl1 = "Dorothy Zbornak"; this.goldenGirl2 = "Rose Nylund"; this.goldenGirl3 = "Blanche Devereaux"; this.goldenGirl4 = "Sophia Petrillo"; } public GoldenGirlsJava( String goldenGirl1, String goldenGirl2, String goldenGirl3, String goldenGirl4 ) { this.goldenGirl1 = goldenGirl1; this.goldenGirl2 = goldenGirl2; this.goldenGirl3 = goldenGirl3; this.goldenGirl4 = goldenGirl4; } public String getGoldenGirl1() { return goldenGirl1; } public void setGoldenGirl1(String goldenGirl1) { this.goldenGirl1 = goldenGirl1; } public String getGoldenGirl2() { return goldenGirl2; } public String getGoldenGirl3() { return goldenGirl3; } public String getGoldenGirl4() { return goldenGirl4; } @Override public String toString() { return "GoldenGirlsJava{" + "goldenGirl1='" + goldenGirl1 + '\'' + ", goldenGirl2='" + goldenGirl2 + '\'' + ", goldenGirl3='" + goldenGirl3 + '\'' + ", goldenGirl4='" + goldenGirl4 + '\'' + '}'; } }


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


Однако, в любом случае, шаблонный код всегда был спорным вопросом для многих разработчиков, и поэтому в 2009 году появился ломбок, который произвел революцию в способах создания структур данных. Он представил концепцию использования обработчика аннотаций и интерпретации конкретных аннотаций, которые придали бы качества, необходимые для наших классов, и поэтому аннотированный класс Lombok мог бы выглядеть следующим образом:

 @Getter @Setter @AllArgsConstructor @ToString public class GoldenGirlsLombok { public String goldenGirl1; private final String goldenGirl2; private final String goldenGirl3; private final String goldenGirl4; public GoldenGirlsLombok() { this.goldenGirl1 = "Dorothy Zbornak"; this.goldenGirl2 = "Rose Nylund"; this.goldenGirl3 = "Blanche Devereaux"; this.goldenGirl4 = "Sophia Petrillo"; } }


И какое-то время люди были очень рады Ломбоку! Наконец необходимость создания всего этого шаблонного кода исчезла. Но это падение продолжалось около 7 лет, когда появился новый игрок, и на этот раз индустрия разработки программного обеспечения этого не ожидала. Он назывался Kotlin и дебютировал в 2016 году с немедленным введением классов данных. Наша реализация золотых девочек в Котлине теперь будет выглядеть так:

 data class GoldenGirls( var goldenGirl1: String = "Dorothy Zbornak", private val goldenGirl2: String = "Rose Nylund", private val goldenGirl3: String = "Blanche Devereaux", private val goldenGirl4: String = "Sophia Petrillo" )


Хотя Kotlin постепенно начал набирать поклонников, Java, с другой стороны, поняла, что на рынке есть что-то еще, и некоторые разработки на этой стороне начали набирать обороты, например проект Loom, который находился в стадии разработки, но на некоторое время остался на заднем плане. Вот почему с выпуском Java 14 в 20202 году в Java появились записи Java, и структуры данных в Java теперь выглядели бы так:

 public record GoldenGirlsRecord( String goldenGirl1, String goldenGirl2, String goldenGirl3, String goldenGirl4 ) { public GoldenGirlsRecord() { this( "Dorothy Zbornak", "Rose Nylund", "Blanche Devereaux", "Sophia Petrillo" ); } }


И по сей день упрощение и сокращение кода, похоже, только продолжаются. Однако с сокращением шаблонного кода концепции полей, геттеров, установщиков, свойств, средств доступа, атрибутов и параметров стали гораздо менее наглядными и их труднее отображать в нашем сознании. Нравится нам это или нет, эти концепции по-прежнему связаны с тем, как JVM работает и организует код с помощью байт-кода. Но упрощение кода на самом деле направлено на то, чтобы сделать код более легким для чтения, а в случае классов данных и записей Java идея также состоит в создании структур данных. которые являются неизменяемыми или частично неизменяемыми.


Записи Java действительно неизменяемы в том смысле, что все содержащиеся в них значения или ссылки не могут быть изменены. Классы данных Kotlin также могут быть действительно неизменяемыми по тем же причинам, но это не обязательно, что в некотором смысле дает разработчикам разрешение создавать сложный и, что хуже всего, изменяемый код. В любом случае все это хорошо, пока нам не понадобится для работы с такими фреймворками, как Spring Framework, которые в значительной степени полагаются на аннотации. Вот пример:

 @Entity @Table(name = "shelf_case") data class Case( @Id @GeneratedValue(strategy = GenerationType.SEQUENCE) val id: Long?, var designation: String?, var weight: Long? ) { constructor() : this(0L, null, null) override fun toString(): String { return super.toString() } }


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

 data class AccountNumbersPassiveDto( @NotNull val accountNumberLong: Long?, val accountNumberNullable: Long?, @DecimalMax(value = "10") @DecimalMin(value = "5") val accountNumber: BigDecimal, val accountNumberEven: Int, val accountNumberOdd: Int, @Positive val accountNumberPositive: Int, @Negative val accountNumberNegative: Int, @DecimalMax(value = "5", groups = [LowProfile::class]) @DecimalMax(value = "10", groups = [MiddleProfile::class]) @DecimalMax(value = "15", groups = [HighProfile::class]) @DecimalMax(value = "20") val accountNumberMaxList:Int )


Этот пример потерпит неудачу, и причина этого в том, что в Котлине нет способа сообщить компилятору, к чему именно мы хотим применить нашу аннотацию. В предыдущем примере с аннотацией @Entity , которая является аннотацией сохраняемости Джакарты, аннотации применяются к полям правильно. Если мы декомпилируем этот код, мы обнаружим следующее:

 public final class Case { @Id @GeneratedValue( strategy = GenerationType.SEQUENCE ) @Nullable private final Long id; @Nullable private String designation; @Nullable private Long weight;


Но для последнего примера, который является примером проверки Джакарты, мы находим следующее:

 public final class AccountNumbersPassiveDto { @Nullable private final Long accountNumberLong; @Nullable private final Long accountNumberNullable; @NotNull private final BigDecimal accountNumber; private final int accountNumberEven; private final int accountNumberOdd;



Это означает, что эти аннотации в полях AccountNumbersDto не повлияли. Нам просто повезло в первом примере. На самом деле это тоже могло потерпеть неудачу. Чтобы указать, куда должна идти наша аннотация, Котлин дает нам возможность использовать цели use-site в качестве префикса к любой аннотации. Разумеется, условие состоит в том, чтобы были соблюдены условия. В этом случае один из способов исправить это — добавить ко всем аннотациям префикс @field следующим образом:

 data class AccountNumbersPassiveDto( @field:NotNull val accountNumberLong: Long?, val accountNumberNullable: Long?, @field:DecimalMax(value = "10") @field:DecimalMin(value = "5") val accountNumber: BigDecimal, val accountNumberEven: Int, val accountNumberOdd: Int, @field:Positive val accountNumberPositive: Int, @field:Negative val accountNumberNegative: Int, @field:DecimalMax(value = "5", groups = [LowProfile::class]) @field:DecimalMax(value = "10", groups = [MiddleProfile::class]) @field:DecimalMax(value = "15", groups = [HighProfile::class]) @field:DecimalMax(value = "20") val accountNumberMaxList:Int )


Если мы теперь попытаемся декомпилировать полученный байт-код с помощью IntelliJ, мы обнаружим, что в результате получается следующий код на Java:

 public final class AccountNumbersPassiveDto { @NotNull @Nullable private final Long accountNumberLong; @Nullable private final Long accountNumberNullable; @DecimalMax("10") @DecimalMin("5") @org.jetbrains.annotations.NotNull


Это означает, что аннотации были применены к полю и наш код работает так, как должно.


На этом этапе вы, наверное, можете себе представить, что у людей, которые долгое время работают на Java, не возникнет проблем с адаптацией к Kotlin, потому что мы все знаем, что такое поля, параметры, свойства и т. д. То, что мы наблюдаем сегодня, и я тоже этому свидетелем, заключается в том, что кажется, что упрощение кода может иметь неприятные последствия. Было немало случаев, когда я осознавал время, потраченное на проекты, пытаясь понять, почему в некоторых проектах или модулях аннотации просто не работают. И все это, кажется, сводится к тому, чтобы не использовать цели использования сайта.


Моя теория, скорее всего, заключается в том, что новые поколения разработчиков, вероятно, будут смотреть на вещи, которые мое поколение не считало сложными и которые они усваивали инстинктивно, как на действительно сложный материал. Так обстоит дело с целями использования сайта. Раньше это были вещи, которые мы могли очень легко визуализировать. Однако в настоящее время возникшая путаница с этим, похоже, в некоторых случаях задерживает разработку проектов в срок. Я бы, конечно, не смог обобщать, но есть потенциал для хорошего и есть потенциал для плохого с Java-записями и классами данных. Трудно сказать, как классы данных и записи будут формировать наше будущее, но ясно одно: из-за этой проблемы другие платформы делают ставку на полное отсутствие аннотаций, как в случае с Ktor.


Я создал документацию по этому поводу на Scribed: Fields-in-Java-and-Kotlin-and-What-to-Expect, а также на Slide-Share: Fields-in-Java-and-kotlin-and-what-to-expect .


Вы также можете найти приведенные мной примеры на GitHub. Пример золотых девочек можно найти здесь: jeorg-kotlin-test-drives , а примеры аннотаций персистентности и проверки Джакарты можно найти здесь: https://github.com/jesperancinha/jeorg-spring-master-test-drives .


Наконец, я также создал видео об этом на YouTube, которое вы можете посмотреть прямо здесь:

Также опубликованоздесь .