Самая скучная часть разработки на уже обкатанном языке — рутинные задачи. Например, показ картинок. Абсолютно рядовая задача, которая в любом проекте обрастает рядом условностей. Приведу в пример несколько таких:
- загруженную картинку нужно отобразить «в кружочке» (кружочки это очень стильно)
- после загрузки сжать изображение до размеров отображения (нечего тратить память)
- изображение нужно сжать до нужного размера и/или пропорции
- в случае неудачной загрузки показать какой-нибудь ресурс по умолчанию
Абсолютно бытовые условности, которые поддерживаются многими готовыми библиотеками (например, для андроида это Picasso, Glide, etc.).
Соответственно, для каждой загрузки нужно передавать набор подобных настроек. Желательно, чтобы кушало это поменьше процессорного времени и памяти, была возможность использовать шаблоны и т.д…
В самом базовом виде набор таких данных будет преставлять из себя примерно следующий класс:
public class ImageSets {
Boolean inCircle = false;
Boolean needFit = false;
Size size = null;
int defaultDrawableResource = -1;
}
Если оставить его так, то работа с ним будет выглядеть примерно следующим образом:
void useCase() {
BaseJavaClass some = new BaseJavaClass();
some.inCircle = true;
some.needFit = true;
some.defaultDrawableResource = 0;
some.size = new Size(128, 128);
System.out.println(some.inCircle);
System.out.println(some.needFit);
System.out.println(some.size);
}
static ImageSets pattern(){
BaseJavaClass some = new BaseJavaClass();
some.inCircle = true;
some.needFit = true;
some.defaultDrawableResource = 0;
some.size = new Size(128, 128);
return some;
}
У подобного подхода есть несколько минусов, вот пара тех, которые вызывают у меня всегда большее раздражение:
- Постоянное упоминание «some.» при вызове. В случае с множественным вызовом (а здесь он как раз актуален), код сильно раздувается.
- Для использования шаблонов нужно каждый раз создавать новый экземпляр шаблона — иначе кто-то может залезть внутрь него и что-то внутри поправить — веселье обеспечено.
С первым недугом принято бороться с помощью сеттеров (можно конечно объявить конструктор со всем набором переменных, но если в Java их больше трех, плюс некоторые из них могут быть необязательными, мы точно получим где-то в программе вызовы, похожие на Some(0,0, null, null, null, -1, 5)):
public BaseJavaClass setInCircle(Boolean inCircle) {
this.inCircle = inCircle;
return this;
}
public BaseJavaClass setNeedFit(Boolean needFit) {
this.needFit = needFit;
return this;
}
public BaseJavaClass setSize(Size size) {
this.size = size;
return this;
}
public BaseJavaClass setDefaultDrawableResource(int defaultDrawableResource) {
this.defaultDrawableResource = defaultDrawableResource;
return this;
}
void useCase(){
BaseJavaClass some = new BaseJavaClass()
.setInCircle(true)
.setNeedFit(true)
.setDefaultDrawableResource(0)
.setSize(new Size(128, 128));
}
static BaseJavaClass pattern1() {
return new BaseJavaClass()
.setInCircle(true)
.setNeedFit(true)
.setSize(new Size(128, 128));
}
Что же, допустим. Но что делать со второй проблемой? Обычно для ее решения используют вспомогательный _Bulder класс, вызов которого будет содержать сеттеры, а вызов основного класса — только геттеры:
public class ImageSets {
protected Boolean inCircle = false;
protected Boolean needFit = false;
protected Size size = null;
protected int defaultDrawableResource = -1;
public Boolean getInCircle() {
return inCircle;
}
public Boolean getNeedFit() {
return needFit;
}
public Size getSize() {
return size;
}
public int getDefaultDrawableResource() {
return defaultDrawableResource;
}
public static final ImageSets pattern = new ImageSetsBuilder()
.setInCircle(true)
.setNeedFit(true)
.setDefaultDrawableResource(0)
.setSize( new Size(128, 128));
}
public class ImageSetsBuilder extends ImageSets {
public ImageSetsBuilder setInCircle(Boolean inCircle) {
this.inCircle = inCircle;
return this;
}
public ImageSetsBuilder setNeedFit(Boolean needFit) {
this.needFit = needFit;
return this;
}
public ImageSetsBuilder setSize(Size size) {
this.size = size;
return this;
}
public ImageSetsBuilder setDefaultDrawableResource(int defaultDrawableResource) {
this.defaultDrawableResource = defaultDrawableResource;
return this;
}
}
Существует много других способов, я привел наиболее распространенные способы решения.
Что же стало с нашими данными? Как так вышло, что класс, содержащий в себе всего 4 переменных, разросся до подобных размеров? Что произойдет при добавлении переменных?
Этот же класс, но на языке Kotlin будет выглядеть следующим образом:
class ImageSets(
val inCircle: Boolean = false,
val needFit: Boolean = false,
val size: Pair<Int, Int>? = null,
val defaultDrawableResource: Int = -1)
Все! Можем использовать:
val pattern = ImageSets(
inCircle = true,
needFit = true,
defaultDrawableResource = 0,
size = 128 to 128)
fun useCase(){
val some = ImageSets(
inCircle = true,
needFit = true,
defaultDrawableResource = 0)
print(some.needFit)
}
Написанный на Kotlin пример дает нам следующие преимущества:
- Все свойства внутри класса (обозначены с помощью val ) изменяются только в конструкторе
- Сам шаблон тоже является свойством и не может переобозначаться
- Объявление экземпляров класса не содержит большого количества скобок и выглядит опрятнее
- Конструктор, который содержит в себе и свойства класса, можно вызывать с использованием конструкции arg = value, что в свою очередь позволяет нам оперировать конструкторами с любым количеством принимаемых переменных.
- Конструкция arg = value позволяет не соблюдать порядок передачи аргументов
- Свойства не обязательно должны быть — свойство size в последнем useCase просто проигнорировано, его не нужно заменять на null, и конструктор от этого не становится непонятным набором 0, null, -1
В дальнейшем обращение к значениям свойств происходит так же, как и к обычным переменным.
- Один класс против двух, один конструктор, прозрачная работа с аргументами по умолчанию, отсутствие геттеров и сеттеров, отсутствие некрасивых возвратов return this
Комментарии (25)
nerumb
16.12.2017 18:38Такими статьями вы только отпугиваете желание других людей смотреть в сторону Kotlin. Мне нравится этот язык, и я считаю его прекрасным инструментом для решения повседневных задач, но то что вы привели это не аргументы. Тот же lombok убирает весь ненужный код, и получается что с ним и Kotlin не нужен?
Дело в том что в Kotlin куда больше прекрасных возможностей для написания красивого и выразительного кода, а читая такие посты у читателей будет создаваться именно негативное впечатление.Guitariz Автор
16.12.2017 19:32Есть такая поговорка — «нет еще задачи по программированию, которую нельзя решить на ассемблере».
Я не утверждаю, что Kotlin — язык, на который непременно нужно полностью перейти с Java. Это личный выбор каждого разработчика. Если для выполнения задач человеку хватает Java — почему нет? Более того, сама парадигма котлина о совместимости с явой — утверждает то же самое.
Lombok сам по себе весит 1400кб, значительно увеличивает время сборки, а так же сложность самого проекта. О влиянии на скорость работы конечного кода не знаю, но подозреваю, что тоже влияет. Kotlin предоставляет тот же функционал, но уже из коробки, гораздо меньшими средствами. Разве это не хорошо?igormich88
18.12.2017 13:56А можно пруфы по времени сборки эквивалентного кода на:
- Чистой Java
- Java + Lombok
- Kotlin
Просто по моему это слишком сильное утверждение, ну и да байткод полученный с использование Lombok насколько я понимаю полностью эквивалентен коду с использованием бойлерплейта.
PS: в следующий раз буду читать комментарии ниже, что бы не дублировать.Guitariz Автор
18.12.2017 13:59Есть сравнение чистой java и kotlin
habrahabr.ru/company/badoo/blog/329026
При инкрементальной сборке kotlin быстрее. Сомневаюсь, что при добавлении lombok проекты на java будут собираться быстрее.
zagayevskiy
16.12.2017 20:53Статья ни о чём. О какой защищённости речь? Примеры и на джаве и на котлине на уровне "начал чего-то учить, решил написать статью".
Vitaljok
16.12.2017 21:28Для чистой Java есть прекрасная библиотека lombok. Ваш длиннющий пример превращается в что-то подобное:
@Data @Builder public class ImageSets { Boolean inCircle = false; Boolean needFit = false; Size size = null; int defaultDrawableResource = -1; }
Guitariz Автор
16.12.2017 21:55Все верно. Я выше уже написал, что все, что есть в Kotlin, есть и в Java, в виде той или иной библиотеки. Вопрос в цене — вес, время сборки, быстродействие.
Space_Cowboy
18.12.2017 13:36Так и котлин отчасти сторонняя библиотека к яве. А по поводу быстродействия, и времени сборки, еще непонятно что быстрее будет java + lombok или котлин, если вы конечно не готовы поделиться ссылочкой на тесты.
Guitariz Автор
18.12.2017 13:42Kotlin это никак не библиотека к Java. Безусловно, он выполняется на JVM, но по поводу библиотеки — это большое заблуждение. Более подробно уже 5 лет назад разжевывалось
habrahabr.ru/post/150104
Могу поделиться замерами, что Kotlin по умолчанию собирается быстрее Java
habrahabr.ru/company/badoo/blog/329026
Тут уже было обсуждение.
Я думаю, не стоит объяснять, почему время сборки Java с подключением lombok будет определенно больше, чем на голой java.
По личным наблюдениям, занимаюсь обоими языками, Kotlin существенно быстрее.Guitariz Автор
18.12.2017 13:48Простите, наоборот, не по умолчанию, а при инкрементальной сборке. Не успеваю поправить.
Space_Cowboy
18.12.2017 13:49Kotlin это никак не библиотека к Java
Только 50% языка это расширеные классы стандартной ява библиотеки. Со своим компилятором, а дальше называйте как хотите)nerumb
18.12.2017 18:45Только 50% языка это расширеные классы стандартной ява библиотеки
Где-то есть статистика?
Space_Cowboy
18.12.2017 13:44Опять статья из разряда, смотрите в котлине нету точек с запятыми буквально на днях был точно такой пост. Всем кому действительно нужны фичи из котлин, на него перейдут и без таких статей, а это все больше и больше продолжает смахивать на какуюто дешевую рекламу.
Guitariz Автор
18.12.2017 13:46Я постарался в первом абзаце описать ситуацию, которая сподвигла меня написать эту статью. Очень жаль, что вы видите статье именно рекламу.
qwert_ukg
18.12.2017 20:09-1Пару слов в защиту Котлина:
Дизайн джавы стар! Многие механизмы давно вросли в языки (свойства, экстнешены, иммутабельность, нулабилити).
Изза старого дизайна не срослось с выводом типов (даймонд синтакс) и вариантностью (in/out удобнее чем вилдкардс), не получилось убрать треугольные скобочки.
Ребята из JetBrains, вроде как много лет непосредственно работают с JVM делайя шустрый комплишн для идеи. Не мало знают о и ее компиляторе, и о дестятке других в придачу (есть из чего выбирать фичи).
Можно псиать библиотеки в рамках старого дизайна (обвешать все аннотациями, и навставлять треугольных скобочек друг в друга), а можно написать свой язык, используя накопленные знания и опыт. А за 6 лет разработки уж наверняка
Не нужно держаться за старое, а если и нужно, то 100%-ный интероп вам поможет :)
qwert_ukg
А мне нравится такая запись:
Приятно читать :)
Guitariz Автор
Тогда уже)
На самом деле можно много чего разного дописать, но я не стал, чтобы не получилось слишком скомкано