Эффективная Java в Kotlin, устаревшие элементы благодаря Kotlin (элементы 3, 4, 16, 40, 61 из 3-го издания)

Kotlin - это огромное улучшение по сравнению с Java. Он был разработан, когда основные недостатки Java были уже известны, поэтому многие из них можно было решить раз и навсегда. Давайте обсудим некоторые моменты из Эффективной книги Java Джошуа Блоха, которые больше не применимы, поскольку мы используем Kotlin.

Правило 3: принудительное применение свойства singleton с помощью частного конструктора или типа перечисления

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

object ThisIsSingleton {
    val prop = 10
    fun method() = 20
}
// Usage
print(ThisIsSingleton.prop) // 10
print(ThisIsSingleton.method()) // 20

Это не только проще в использовании и декларировании, но и совершенно безопасно. Фактически, Kotlin под капотом использует первый описанный в Эффективном шаблоне Java для правильного объявления синглтона.

Правило 4: Обеспечьте невозможность создания экземпляров с помощью частного конструктора

Смысл этого элемента в том, что Java не допускает функций верхнего уровня, поэтому все такие функции помещаются как статические члены в классы util:

// Java
class StringUtils {
    public static String trim(s: String) { /*...*/ }
}

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

fun trim(s: String): String { /*...*/}

Хотя в этом случае мы бы предпочли определить функцию расширения верхнего уровня:

fun String.trim(): String { /*...*/}

Оба скомпилированы в JVM в статический метод в классе, который не может быть инициализирован.

Другой альтернативой является объявление объекта:

object StringUtils {
    fun trim(s: String): String { /*...*/}
}

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

interface CarFactory {
    val prize: Int
    fun makeCar(): Car
}

object Fiat126PFactory: CarFactory {
    override val prize: Int = 500
    override fun makeCar(): Car = TODO()
}

object OpelAstraFactory: CarFactory {
    override val prize: Int = 2_500
    override fun makeCar(): Car = TODO()
}

object FerrariFactory: CarFactory {
    override val prize: Int = 500_000
    override fun makeCar(): Car = TODO()
}

val factories = listOf(
    Fiat126PFactory, 
    OpelAstraFactory, 
    FerrariFactory
)
val cheapestCar = factories.minBy { it.prize }?.makeCar()

Правило 16. В общедоступных классах используйте методы доступа, а не общедоступные поля

Это наиболее явное улучшение по сравнению с Java. Что ж, в Java у нас были поля, а в Kotlin - свойства. Они выглядят почти одинаково, но ключевое отличие в том, что для свойств Kotlin мы всегда можем установить пользовательский сеттер. Значит, они инкапсулированы. Чтобы разрешить это в Java, нам нужно использовать field, getter и setter. Давайте посмотрим на это на нескольких примерах. Следуя за свойством Котлина:

var name: String = "Marcin Moskala"

является эквивалентом следующего поля Java и методов доступа:

// Java
@NotNull
private String name = "Marcin Moskala";

@NotNull
public String getName() {
   return name;
}

public void setName(@NotNull String name) {
   Intrinsics.checkParameterIsNotNull(var0, "<set-?>");
   this.name = name;
}

Каждое использование свойства фактически использует сеттеры и геттеры. Мы всегда можем установить собственные сеттеры и геттеры:

var name: String = "Marcin Moskala"
    get() = field.trim()
    set(value) {
        if(value.isNotEmpty())
            field = value
    }

который будет компилироваться примерно так:

// Java
@NotNull
private String name = "Marcin Moskala";

@NotNull
public String getName() {
   return StringsKt.trim(name).toString();
}

public void setName(@NotNull String value) {
   if (value.length() > 0) {
      name = value;
   }
}

На этом этапе Kotlin обеспечивает поведение, предложенное Effective Java, и его трудно обойти. Мы всегда можем изменить метод установки и получения свойств, не изменяя использование в Kotlin.

Пункт 40: постоянно используйте аннотацию Override

В отличие от аннотации Java Override, в Kotlin требуется модификатор override. Это вершина того, что предлагает книга.

Пункт 61: предпочитайте примитивные типы упакованным примитивам

Вы когда-нибудь видели примитивный шрифт в Котлине? Я часто вижу их, когда читаю скомпилированный код Kotlin. Дело в том, что каждый тип в Kotlin действует как коробочный тип, но, когда это возможно, компилятор Kotlin использует скрытые примитивы. Это означает, что Kotlin Int - это int, когда это возможно, и Integer, когда требуются некоторые характеристики ООП (например, допускание значения NULL или использование для универсального). Можно было бы, вероятно, подумать о конкретном Int использовании, позволяющем компилятору использовать примитивы под капотом, но я считаю, что это абсолютно ужасная идея. Потенциальное улучшение производительности и памяти будет незначительным, а хаос, внесенный в код, будет существенным. Я могу придумать единственный разумный случай, когда нам нужно работать с набором примитивов. Я описал это вместе со случаями использования Integer вместо int в другой статье. А пока просто помните, что вам не нужно беспокоиться о примитивах в Kotlin, потому что компилятор Kotlin сделает за вас тяжелую работу и оптимизирует ваш код.

Резюме

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

Эффективный Котлин

Эта статья является частью серии «Эффективный Котлин», связанной с книгой, которую я собираюсь опубликовать. Вы найдете там набор лучших практик для удобочитаемости, масштабируемости и производительности в Kotlin:



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

Вам нужна мастерская Kotlin? Посетите наш сайт, чтобы узнать, что мы можем для вас сделать.

Чтобы быть в курсе отличных новостей на Kt. Академия , подписывайтесь на рассылку новостей , следите за Twitter и следите за новостями.

Чтобы сослаться на меня в Twitter, используйте @MarcinMoskala. Используйте ссылку ниже, чтобы подписаться на рассылку новостей: