Для отображения списка тем, войдите на форум или зарегистрируйтесь.
Сейчас на форуме 11 посетителей
Неизвестный Бобер
Неизвестный Лось
Неизвестный Павлин
Неизвестный Крокодил
Неизвестный Суслик
Неизвестный Страус
Неизвестный Пингвин
Неизвестный Слон
Неизвестный Носорог

Автор Тема: Свободное общение  (Прочитано 430559 раз)

Скрывать неподписанных -

0 Пользователей и 1 Гость просматривают эту тему.

Монс| RETURN TO MONSE

2019 Окт.. 20, 17:41 [#60210]
В каком контексте ещё движение может быть расточительным, кроме как занятие лишнего времени?
Только в контексте того что оно ненужно, иначе это будет дополнительное время, а не лишнее  :nu
 
Им ссыт под себя при упоминании Жирбоя
Горосиси потеют от того что шичей нагибают
Горосися котируют слабиралов
Шанкс пугает Горосись Тичем/Шебеком.
Горосиси сильно потеют с союза двух йонко.
Горосиси и Им делают ставку на древнее говнооружие.
Горосися уже пешка в игре Тича
Горосиси кровь из жопы, ссутся сообщения Вегапупка.
Горосиси впятером ничего не могут сделать с мугиварами
Горосиси разлетаются от вспышки КВ уровня Шункса

hommius| Истинный фанат Дабуры

2019 Окт.. 20, 17:41 [#60211]
Только в контексте того что оно ненужно, иначе это будет дополнительное время, а не лишнее  :nu
У меня BSOD

Дополнительное время  :pepe_clown
 

Монс| RETURN TO MONSE

2019 Окт.. 20, 17:46 [#60212]
 :catspalm

Короче, любой может тупо зайти и посмотреть че на сканах и в доп комментария он не нуждается.

Пусть каждый сам читает шо там написано и придумывает или понимает происходящее как хочет.

 :respect
 
Им ссыт под себя при упоминании Жирбоя
Горосиси потеют от того что шичей нагибают
Горосися котируют слабиралов
Шанкс пугает Горосись Тичем/Шебеком.
Горосиси сильно потеют с союза двух йонко.
Горосиси и Им делают ставку на древнее говнооружие.
Горосися уже пешка в игре Тича
Горосиси кровь из жопы, ссутся сообщения Вегапупка.
Горосиси впятером ничего не могут сделать с мугиварами
Горосиси разлетаются от вспышки КВ уровня Шункса

hommius| Истинный фанат Дабуры

2019 Окт.. 20, 17:50 [#60213]
Пусть каждый сам читает шо там написано и придумывает или понимает происходящее как хочет.
Ну да, но тут два варианта: либо воспринимать как задумал автор, либо выдумывать какую-то хрень про "Нетеро мог бы пиздиться намного эффективнее если бы не делал лишних движений" :uvojenie
 

Монс| RETURN TO MONSE

2019 Окт.. 20, 17:51 [#60214]
Ну да, но тут два варианта: либо воспринимать как задумал автор, либо выдумывать какую-то хрень про "Нетеро мог бы пиздиться намного эффективнее если бы не делал лишних движений" :uvojenie
Все никак не можешь смирится что твое мнение не самое правильное?  :pepe_clown

Я думал такое после школы проходит.  :snide
 
Им ссыт под себя при упоминании Жирбоя
Горосиси потеют от того что шичей нагибают
Горосися котируют слабиралов
Шанкс пугает Горосись Тичем/Шебеком.
Горосиси сильно потеют с союза двух йонко.
Горосиси и Им делают ставку на древнее говнооружие.
Горосися уже пешка в игре Тича
Горосиси кровь из жопы, ссутся сообщения Вегапупка.
Горосиси впятером ничего не могут сделать с мугиварами
Горосиси разлетаются от вспышки КВ уровня Шункса

Монс| RETURN TO MONSE

2019 Окт.. 20, 17:52 [#60215]
Ппц Хом, сам додумывает текст и смыслы за автора и потом втирает что этот его манявзгляд единственно верный, ой все

 :pepe
 
Им ссыт под себя при упоминании Жирбоя
Горосиси потеют от того что шичей нагибают
Горосися котируют слабиралов
Шанкс пугает Горосись Тичем/Шебеком.
Горосиси сильно потеют с союза двух йонко.
Горосиси и Им делают ставку на древнее говнооружие.
Горосися уже пешка в игре Тича
Горосиси кровь из жопы, ссутся сообщения Вегапупка.
Горосиси впятером ничего не могут сделать с мугиварами
Горосиси разлетаются от вспышки КВ уровня Шункса

hommius| Истинный фанат Дабуры

2019 Окт.. 20, 17:52 [#60216]
Я думал такое после школы проходит. 
Ты уже 11/12 лет назад закончил, как видишь не прошло :pesik
 

Монс| RETURN TO MONSE

2019 Окт.. 20, 17:52 [#60217]
Ты уже 11/12 лет назад закончил, как видишь не прошло :pesik
Хорош обижаться, пусть ты глупышка но мы тебя любим  :love

просто чел| Трушная оппозиция. Кручу верчу наебать хочу.

2019 Окт.. 20, 18:18 [#60218]
не хочу пиздить эредина на мельдонии  :ohunyanno

 :dalton


просто чел| Трушная оппозиция. Кручу верчу наебать хочу.

2019 Окт.. 20, 18:19 [#60219]
я понял, что лучше пасты чела читать, чем ваш срач  :nu

о  меня звали?


Монс| RETURN TO MONSE

2019 Окт.. 20, 18:21 [#60220]
о  меня звали?
Да, хотим почитать твои пасты  :pepe_clown

просто чел| Трушная оппозиция. Кручу верчу наебать хочу.

2019 Окт.. 20, 18:21 [#60221]
В очередной раз набирая на клавиатуре в Android Studio строчки кода с циклом for, поймал себя на мысли, что делал это множество раз и уже знаешь каждый следующий оператор, который идет дальше. Задача простая: выбрать из списка множество элементов, удовлетворяющее поставленному условию. Вроде бы ничего сложного, все просто: цикл по списку, внутри условие и добавление нового элемента в результирующий список. Еще один стандартный статический метод в классе с утилитами. Какой он там по счету уже...- 5ый или 10ый? Буквально 3-4 строки кода, которые сделают свое дело, НО почему-то удовлетворения от выполненной задачи нет. Где-то там глубоко есть ощущение, что можно делать это проще. Там же рядышком лежат классы-утилиты для работы со строками и датами. Сколько их уже было за все время?

А тут еще на днях Петя/Ваня/Дэвид сказал, что они начинают новый проект под Android и будут писать его исключительно на Kotlin. Kotlin, а не Java. Чем этот язык лучше Java? Сколько уже было этих пустых никому не нужных попыток писать код под Android на PHP/C/C++/С#/Python, и где все это сейчас? Возможно, в этот раз все по-другому, и стоит обратить на него внимание? Ну что же, посмотрим, хотя я очень слабо верю во что-то путнее.

Разрабатывает его JetBrains и уже давненько, т.е. за это время их желание создать свой язык не иссякло. Это похвально и заслуживает уважения. Мне нравится их IDEA & AS. Более того, главной целью для себя они ставят полностью перевести разработку внутри компании с Java на Kotlin, и хотят, чтобы он, как и Java, использовался в industry. А вот некоторые называют его прямо «Swift под Android», вот же ж юмористы! И не просто люди с улицы, а сами разработчики Kotlin! Утверждают, что про NPE в рантайме можно забыть, что можно расширять стандартные классы из SDK, что можно лямбды и анонимные функции писать и даже передавать функции как параметры в методы! И даже точку с запятой убрали из синтаксиса (кстати, вот с этим я согласен на все 100%). А это прямо-таки из моего PHP-ного прошлого — String Templates — по таким вещам иногда скучаешь. Забавно, конечно, но вряд ли это все как-то может пригодиться в Android-разработке. Хм, у них есть плагин под IDEA/AS и даже специальный плагин под Андроид проекты. Вот это сейчас надо посмотреть.

Интересно выходит — Kotlin на 100% совместим с Java, и плагин позволяет java-код в один клик трансформировать в kotlin-код, и это как раз можно проверить достаточно быстро. Создадим-ка пустой Android-проект с одной Activity и Fragment внутри, и еще один простой дата-класс тоже не помешает.

Берем шаблонную Activity:

package com.testkotlin;

import ...

public class TestActivity extends AppCompatActivity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       setContentView(R.layout.activity_test);
       Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
       setSupportActionBar(toolbar);

       FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
       fab.setOnClickListener(new View.OnClickListener() {
           @Override
           public void onClick(View view) {
               Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                       .setAction("Action", null).show();
           }
       });
   }
}

и с помощью действия «Convert Java File to Kotlin File»:

на выходе получаем такой вот код:

package com.testkotlin;

import ...

class TestActivity : AppCompatActivity() {

   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_test)
       val toolbar = findViewById(R.id.toolbar) as Toolbar
       setSupportActionBar(toolbar)

       val fab = findViewById(R.id.fab) as FloatingActionButton
       fab.setOnClickListener { view ->
           Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                   .setAction("Action", null).show()
       }
   }
}

Выглядит неплохо, и вроде как все понятно. Получается, что из-за 100%-ой совместимости с Java новая TestActivity на Kotlin спокойно наследуется от AppCompatActivity, которая лежит в Support Library, а не в SDK. Ну что же, это довольно-таки серьезно облегчает переезд на рельсы Kotlin и делает использование Java-библиотек безболезненным. Безусловно это плюс, пожалуй, стоит записать очко в пользу Kotlin.

Теперь проверим, как это сработает на обычном дата-классе:

public class Message {
   private String text;
   private boolean isRead;

   public Message(String text, boolean isRead) {
       this.text = text;
       this.isRead = isRead;
   }

   public String getText() {
       return text;
   }

   public boolean isRead() {
       return isRead;
   }
}

и конвертнем его в kotlin-файл:

class Message(val text: String, val isRead: Boolean)

Одна строка кода по сравнению с 14-ю в Java-варианте! А если еще перед описанием класса добавить data, то автоматом получаем возможность делать копии объектов с помощью метода copy(). В качестве параметров он может принимать новые значения полей объекта. Запишите еще очко в пользу Kotlin.

Это ж на сколько время-то экономится?!?! Вот такого твиста я не ожидал, и, бегло (ну, как бегло...часика 2) пробежавшись по разделу «Classes and Objects» в документации, я узнал, что:

    для создания экземпляра класса нам не нужно ключевое слово new;
    никаких тебе extends или implements, для этого теперь используется «:»;
    класс может иметь один primary constructor и один/несколько secondary constructors;
    у primary constructor нет тела, но внутри него можно описать блок инициализации init;
    используются ключевые слова var (mutable property)/val (read-only property) для декларации и инициализации properties прямо в объявлении конструктора. Ключевые слова val\var используются и при объявлении обычных локальных переменных;
    OMG!, здесь есть named arguments и дефолтные значения для них;
    можно описывать кастомные accessors — круть! очень полезная штука;
    теперь не нужно явно вызывать getter/setter для получения/присвоения значения проперти — обращение происходит просто по имени — тоже здОрово!
    по умолчанию область видимости для классов/методов/пропертей — public;
    по умолчанию все классы в Kotlin final и нужно использовать open-аннотацию для класса, если необходимо иное поведение — к этому придется немного привыкать;
    интерфейсы теперь могу реализовывать методы!!! Т.е. класс может имплементить два интерфейса, которые имеют различную реализацию метода с одинаковой сигнатурой, и я смогу выбрать, где какой из методов использовать. Вот это реально круто! Очень давно об этом мечтал;
    вариативность женериков (generics) реализована с помощью in/out-кейвордов;
    реализовать anonymous inner classes можно через object expressions & object declarations;
    всеми любимый Singleton легко и просто реализуется с помощью object declaration;
    Java static methods == object declaration + companion object;
    делегирование реализовано на уровне декларации класса с помощью by-clause, компилятор сам сгенерирует необходимые дублирующие методы — любители делегирования возрадуйтесь, теперь не нужно дублировать вручную методы!

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

А еще можно взять java-код и скопипастить его в kotlin-файл и IDE сама предложит его конвертнуть. Кстати, на Java можно и в kotlin-файлах спокойно писать код, и все будет работать — это как еще один бонус 100%-й совместимости. Там же с помощью IDE полученный код можно упростить и привести его к kotlin-style. Это здорово поможет ускорить обучение и сдвинуть мышление в сторону Functional Programming(FP).

Все это выглядит здорово, местами для закоренелого object-oriented developer непривычно, но ведь красиво же! И, главное, что на выходе я получаю максимально оптимизированный java-код. Да уж, пессимизма по отношению к этому языку во мне поубавилось.

И я уже было собирался ложиться спать (на часах около 3-х ночи), но по глупости взял и посмотрел вот этот небольшой доклад по теме от Jake Wharton и понял, что слишком я перестарался с игнорированием функционального программирования. Но этот пробел я буду восполнять уже завтра, пардон, сегодня. Еще и выспаться нужно успеть, и на проекте ждет меня куча бизнес-логики — опять циклы/условия и статические методы — а дедлайны никто не отменял. К тому же, будет что рассказать хлопцам за чашечкой кофе по Kotlin-у и непрозрачно намекнуть моему РМ-у, что было бы неплохо для Waverley (компания, где я работаю) начинать подыскивать Android-проект, где заказчику будет все равно на Kotlin-е мы его напишем или на Java. И разработчикам профит, и компания может себе в копилку еще одну технологию записать.

просто чел| Трушная оппозиция. Кручу верчу наебать хочу.

2019 Окт.. 20, 18:21 [#60222]
Необходимо сделать лирическое отступление и констатировать факт, что со времени написания оригинальной серии статей по Kotlin и публикацией их на данном ресурсе, прошло уже достаточное время. Время достаточное для того чтобы: а) вышла обнова на Kotlin 1.1; б) наш любимый Гугль решил-таки начать нативно поддерживать Java 8 под Android. Более того, второе событие произошло непосредственно в день выхода первой части этой серии статей — вот прям как звезды сошлись на небе.

Не знаю, какое из событий меня больше порадовало, но в любом случае пришлось кой-чего переписывать...к примеру, вот эти самые строки. Безусловно, я рад за платформу и благодарен Гуглу за такой ответственный шаг, НО Kotlin стал еще вкуснее и приятнее взору, пальцам и мозгам. Для тех, кого цепанула вторая новость, просим милости сюда изучать, чем нам это грозит. А для тех, кто, так же как и я, пропустил событие 1-ого марта в Котляндии, рекомендую не дочитывать эту фразу, а сразу пропустить следующий абзац...

Вы все еще здесь? А может ну его, этот Kotlin, и на боковую, а?;) Ах, да, чуть не забыл — дабы сократить поток негодования на тему «ах, да как ты/вы мог(ли) вот это не упомянуть или вот это?!», сразу ответственно заявляю, что данный артефакт не является мануалом к языку ни в коем случае. Я всего лишь описываю то, что МНЕ понравилось больше всего, когда я пропустил через себя Kotlin. То, что вот я бы непосредственно практически каждый день с радостью использовал в трудовых буднях.

Так-с...на чем я там остановился в прошлый раз? Ах, да, по диагонали изучил классы и хотел посмотреть аспекты of Functional Programming. Но я думаю, что правильнее все-таки будет сначала на базовый синтаксис взглянуть и узнать, что там есть вкусного. Есть у меня предчувствие, что вечерночь не пройдет зря. Поехали...
Null Safety

Начнем, пожалуй, с такой штуки как nullability. В отличие от Java переменная в Kotlin не может содержать null, если компилятор об этом не знает. Т.е. объявляя переменную, нужно явным образом указать может она быть nullable или нет. Такое простое решение — на уровне системы типов заставить разработчика заранее подумать о том, как должна вести себя переменная, какие значения она может принимать. Все сделано для того, чтобы максимально исключить возможность появления NPE (Null Pointer Exception...хотя кому это я объясняю???) в коде, и особенно в рантайме. Но все-таки NPE могут возникнуть в таких случаях:

    явный вызов throw NullPointerException()
    ошибка во внешнем Java-коде
    использование специфичного оператора !!
    Как же это все работает? Возьмем к примеру следующий код:

var strNotNull : String = "Hello"
strNotNull = null //compilation error

Во второй строке мы получим ошибку при попытке компиляции. Для того, чтобы иметь возможность записать null в переменную, нам нужно сделать следующее:

var strNull : String? = "Hello"

Теперь все сработает. Кстати, знание того, что в переменной НИКОГДА не будет null избавляет нас от дурацких проверок каждый раз, когда, к примеру, нам нужно получить длину строки. Можно смело делать вот так:

val length = strNotNull.length

и нам за это ничего не будет. А что же делать в случае, когда у нас может быть null в переменной? Просто использовать safe-call operator — ?.

val length = strNull?.length

И в данном случае в переменной length будет сохранена либо длина строки, либо null, в зависимости от состояния strNull. И тип переменной будет автоматически приведен к Int?. Если же мы не хотим возвращать null, то можно использовать Elvis operator:

val length = strNull?.length ?: -1 //length is instance of Int, but not Int?

Можно построить целую цепочку таких safe-calls, к примеру:

message?.sender?.birthdate?.year

Если же нам нужно выполнить блок операций для случая, когда переменная точно не равна null, то можно использовать функцию let:

//say good-bye to if (message != null) {}
message?.let{
..
}

Отличнейшая альтернатива заезженным блокам if/else. Настоятельно рекомендуется к использованию.

Как по мне, то очень интересное и элегантное решение проблемы с NPE в рантайме. По факту большинство NPEs вылазит именно из-за того, что мы забываем проверить на null или думаем, что вот в этом самом месте нам никогда не придет null или даже оставляем это «на потом» в спешке. Хотел было засчитать еще одно очко в пользу Kotlin, но решил, что данное решение заслуживает двух.;)
Smart casts & type checks

Как бы так поделикатнее описать свои мысли о реализации of smart casts & type checks in Kotlin и при этом не сильно обидеть Java? Одним словом — НАКОНЕЦ-ТО!!! Забудьте об этих дурацких дублирующих друг друга операциях с приведением типов как в примере ниже:

if (obj instanceOf String) len = ((String) obj).length();

Каждый раз, когда я пишу подобный код, мне хочется плакать....Ну почему компилятор не может догадаться, что если я проверяю переменную на принадлежность к какому-то типу, то при выполнении этого условия я хочу использовать именно этот интерфейс?! Ведь из-за этих ограничений Java-компилятора рождаются кошмарные конструкции и блоки кода, на который без слез смотреть невозможно.

Но ребята в JetBrains похоже услышали мольбы разработчиков и реализовали систему of smart casts. Вышеуказанный код на Kotlin выглядит следующим образом:

if (obj is String) len = obj.length

А теперь представьте, что вам нужно выполнить несколько вызовов переменной, и посмотрите, как это красиво можно сделать с помощью функции with:

with(obj) {
    val oldLen = length // what happens here is obj.length() is being called
    val newLen = replace(" ", "").length //and obj.replace(“ “, “”).length() here    
}

Последний раз такую вкусность я использовал в Pascal/Delphi, и это было чертовски удобно, за такими вещами скучаешь. Особенно часто я вспоминаю об этом, когда инициализирую какой-нибудь объект типа Paint, и надо сделать несколько вызовов подряд, чтобы задать шрифт, цвет, тип заливки, антиалиасинг и прочее.

Тем не менее нужно помнить, что automatic smart cast работает в том случае, если компилятор точно уверен, что между проверкой и использованием переменной она не поменяет значение.

Ну а как быть, если мы хотим привести переменную к какому-то типу? Kotlin предлагает на выбор две опции:

    unsafe cast (небезопасныйое секс, пардон, приведение) с помощью infix operator as
    safe cast с помощью infix operator as?

В случае первого при попытке привести null value к какому-то типу выкинет исключение. Второй же оператор просто вернет null в результирующую переменную. Я бы рекомендовал использовать именно его.

Что мы имеем в итоге? Красивый, элегантный, читабельный код, не перегруженный синтаксисом. Прямо вот не могу удержаться и накидываю еще 2 очка в пользу Kotlin. Прости, Java, сегодня, видимо, не твой счастливый день.
Type aliases (доступно в версии 1.1)

Раз уж мы поговорили про приведение типов, то целесообразным будет упомянуть возможность объявления альтернативных имен для существующих типов. Что-то подобное я встречал давно в Pascal. Но в Kotlin эта фича работает и для функций, что делает ее особенно интересной! Что конкретно нам это дает?

    мы можем объявлять «свои типы данных», к примеру, сократить длинный женерик до короткого дружелюбного имени

    typealias BestHashMapEver = HashMap<String, Int>
    val myMap = BestHashMapEver()

    Да, знаю, имя тут вышло не совсем короткое, но вы меня поняли.;)

    typealias Str = String
    val myStr:Str = “Hello DOU!”

    мы можем объявлять псевдонимы для функций и использовать их при описании параметров

    typealias BestFunctionEver = (Int, Int) -> Boolean
    fun myFun(f: BestFunctionEver) : f(10, 11)

    myFun{x, y -> x > y}
    val bestFun: BestFunctionEver = {x, y -> y > x}
    myFun(bestFun)

Следует заметить, что type alias не генерирует нового типа данных. На уровне компилятора он расценивается как тот самый основной тип, что делает их взаимозаменяемыми. Я бы предложил следующие ситуации, когда можно использовать type aliases:

    меняющиеся структуры данных;
    меняющиеся сигнатуры функций;
    тестирование;
    создание flavour-билдов;
    смысловое разделение типов данных и функций по именам/категориям.

Не могу сказать, что вот прямо конкретно этой возможностью я бы пользовался каждый день, но отторжения она у меня не вызвала, а скорее наоборот. Я использовал ее в Pascal/Delphi и не вижу, что может мне помешать сейчас! Однозначно +1 в корзинку Kotlin.
Ranges & Controlling Flow

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

if (a > b) {
   max = a;
} else {
   max = b;
}

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

val max = if (a > b) a else b

Чертовски удобно ведь! Если надо выполнить больше кода в блоках условия — нет проблем, но последняя строка должна возвращать результат работы в переменную. Этот же самый принцип работает для when expression. when — это тот же switch из Java и других С-подобных языков. Но вот только when дает намного больше свобод и вариативности использования. В сравнении с ним switch выглядит мелковато и абсолютно не юзабельно. Да, и забудьте про ненавистный break для каждой из веток условий.

Я не случайно объединил ranges и Flow Control Structures в один раздел. Нельзя рассказывать про блоки условий и циклы и не затронуть ranges, ведь именно они являются еще одним замечательным бонусом, который будет облегчать вам жизнь. Признаться, я еще со времен Паскаля очень любил ranges и считал их очень удобным решением, и все никак не мог понять, почему их нет как отдельного типа данных в том же С/С++ или в РНР(с которым в обнимку провел 9 лет) или в JS и, конечно же, в Java. И вот в Kotlin я снова с ними повстречался, но вот только варианты их использования стали шире и удобнее по сравнению с Паскалем. К примеру, range можно сохранить в переменную и использовать в блоке условия:

val myRange = 1..10
if (x in myRange) { //you might want to use !in to check if value IS NOT in range

}

Естественно, что вместо чисел 1 и 10, могут быть использованы переменные или даже вызовы функций. При использовании в цикле for можно указывать направление движения и величину шага:

for (i in 1..4 step 2) print(i) // prints "13"
for (i in 4 downTo 1 step 2) print(i) // prints "42"

Ах, да, еще в циклах можно использовать break & continue с метками для прыжков между циклами как и в Java.

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

//just a helper function that checks whether int value is even or not?
fun Int?.isEven() : Boolean = this?.mod(2) == 0

val range = 10..20;

//function with when-construction that takes a parameter of Any kind
fun test1(x:Any?) {
    when(x) { //here we pass in x to when
        null -> print("x is null")
        0,1,2 -> print("x = $x")           
        is CharRange -> print("x is a CharRange $x")
        in range -> print("x is in range $range, x = $x")
        is String -> print("x is a String, x = \"$x\"")
        else -> print("x is of ${x?.javaClass}, x = $x")
    }
    print("\n")
}

//function with when-construction that does not take a parameter and may substitute if/else-if blocks completely
fun test2(x:Any?) {
    when { //no value is passed in
        x == null -> print("x is null")
        x in 0..2 -> print("x = $x")
        x is CharRange -> print("x is a CharRange $x")
        x in range -> print("x is in range $range, x = $x")
        (x is Int? && x?.isEven() ?: false) -> print("x is even integer, x = $x")
        x is String -> print("x is a String, x = \"$x\"")
        else -> print("x is of ${x?.javaClass}, x = $x")
    }
    print("\n")
}

fun main(args: Array<String>) {
    test1(null) // prints "x is null"
    test1(2) // prints "x = 2"
    test1('A'..'Z') // prints "x is a CharRange A..Z"
    test1(15) // prints "x is in range 10..20, x = 15"
    test2(22) // prints "x is even integer, x = 22"
    test1("HELLO!") // prints "x is a String, x = "HELLO!""
    test1(101) // prints "x is of class java.lang.Integer, x = 101"
}

Сколько бы вы баллов накинули Kotlin за этот раздел?

Изначально на этом месте я подводил итоги по базовым вещам и говорил о том, какой же все-таки Kotlin классный, как лаконичнее код выглядит с применением вышеизложенного и прочее бла-бла-бла, НО, как мы знаем, 1-ого марта вышло обновление 1.1., и оно нам принесло:

Coroutines (доступно в версии 1.1)

Правильнее даже сказать: «доступно с версией 1.1», потому что корутины не входят в сам язык на данный момент, а являются экспериментальной функциональностью и вынесены в отдельный артефакт. Для использования оных нужно в build.gradle файле проекта указать:

kotlin {
    experimental {
        coroutines ‘enable’
    }
}
dependencies {

    compile "org.jetbrains.kotlinx:kotlinx-coroutines-core:0.14"

}
//по непонятным для меня причинам в один прекрасный момент gradle перестал
//находить артефакт с корутинами, и пришлось явным образом указать, где их стоит искать
repositories {
   maven { url "https://kotlin.bintray.com/kotlinx/" }
}

И теперь нам становятся доступны корутины. Что же это такое? В каком-то смысле — это легковесная нить, но очень дешевая в обслуживании, которую можно приостанавливать и возобновлять. Она может состоять из вызовов других корутин, которые в свою очередь могут выполняться в совершенно разных нитях. Одна нить (Thread) может обслуживать множество корутин. Kotlin предоставляет несколько механизмов использования корутин, но я рассмотрю только парочку наиболее интересных в контексте Android-разработки.

Первый вариант использования — это функция launch():

launch(CommonPool){
...
}

CommonPool представляет собой пул повторно используемых нитей и под капотом использует java.util.concurrent.ForkJoinPool. Ниже приведены два варианта одного и того же решения: блокирующие вызовы (никогда так не делайте!) и неблокирующие на основе корутин.

//this is a blocking example, isn’t it? ;)
println("Start")
for(i in  1..100) { //our main thread sleeps for 10 seconds...arghhh!!!
    Thread.sleep(100)
}
println("Hello!")
println("Stop")

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

11:22:41.250 419-419/com.test I/System.out: Start
11:22:51.269 419-419/com.test I/System.out: Hello!
11:22:51.269 419-419/com.test I/System.out: Stop

А вот неблокирующий вызов:

//this ain’t a blocking example, isn’t it?;)
println("Start")
launch(CommonPool) {
    for(i in  1..100) {
        delay(100) //hey, hey, what are we doing here?
    }
    println("Hello!")
}
println("Stop")

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

11:27:32.824 6212-6212/com.test I/System.out: Start
11:27:32.838 6212-6212/com.test I/System.out: Stop
11:27:42.884 6212-6238/com.test I/System.out: Hello!

Зоркий глаз и пытливый ум уже заметили, что для задержки в 100 миллисекунд внутри функции launch() использовался вызов delay(). Он аналогичен Thread.sleep(), но используется в контексте корутины и блокирует ее, а не основной поток. Важно запомнить, что все конструкции, специфичные для корутин, могут быть использованы только в их пределах.

Ахх, я вижу как у вас загорелись глаза и начали роиться мысли о том, как с помощью одной такой конструкции можно заменить связку Thread-Handler(UI)? Как же нам данные из корутины корректно передать в UI-поток? Самые невнимательные могут предложить такое решение:

launch(CommonPool) {
    for(i in  1..100) {
        delay(100)
    }
    myTextView.text = "Hello!" //can’t touch UI-stuff in non-UI thread!
}

и сразу получат по рукам — мы же находимся в рабочей нити. А внимательные сделают так:

launch(CommonPool) {
    for(i in  1..100) {
        delay(100)
    }
    runOnUiThread { myTextView.text = "Hello!" } //will work, but it looks weird
}

и это будет работать, но выглядит оно как-то не очень, согласитесь. И прежде чем показать, как это можно сделать «по красоте», необходимо разобраться с функцией async(). Она очень похожа на launch(), с одним лишь отличием, что возвращает результат работы экземпляром Deferred, а у него есть метод await(), который собственно и возвращает результат работы функции.

Давайте смотреть, как это выглядит. Предположим, нам нужно посчитать факториалы двух чисел и получить их сумму. Не спрашивайте меня, кому это может понадобиться, просто мне так захотелось.;) Для этого опишем следующую корявенькую функцию подсчета факториала (+1 в карму первому комментатору данной реализации, а все желающие углубить свои познания о факториале милости просим сюдой):

fun factorial(num : Int) : Deferred<Long> {
   return async(CommonPool) {
           var f:Long = 1
           for (i in 2..num) {
               f *= i
               delay(100) //this is just for purposes of an experiment
           }
           f //it's not a typo, it's a <strong>'return'</strong> statement!!!
       }
}

Внутри функции запускается async-корутина, которая вернет нам значение типа Long. Я поставил задержку в цикле, чтобы показать одну примечательную деталь работы корутин (о ней чуток позже). Теперь сделаем вызов этой функции и посмотрим, как оно отработает:

println("Start")
launch(CommonPool) {
   val f5 = factorial(5)
   val f25 = factorial(25)
   println((f5.await() + f25.await()))
}
println("Stop")

Результаты работы следующие:

11:42:35.050 26517-26517/? I/System.out: Start
11:42:35.061 26517-26517/? I/System.out: Stop
11:42:37.486 26517-26530/com.test I/System.out: 7034535277573963896

А теперь о том, на чем я бы хотел заострить ваше внимание. Если вы заметили, то разница между последними двумя выводами в консоли — около 2.5 секунд. Хотя 5! будет посчитан за ~0.5 секунды. А вот 25! как раз просчитается где-то за 2.5 секунды. Таким образом функция вывода println() не будет выполнена пока не вернутся результаты из всех корутин, т.е. по факту — пока не отработает самая долгая из них.

Отлично, с этим разобрались, и, собственно, теперь можем вернуться к поставленной выше задаче: как результаты работы корутины правильно передать в UI-поток. И в этом нам поможет модуль kotlinx-coroutines-android, который предоставляет UI контекст для корутин — замену тому самому CommonPool, что мы использовали в примерах выше. Для этого закидываем этот модуль в зависимости:

dependencies {
    …
    compile "org.jetbrains.kotlinx:kotlinx-coroutines-android:0.14"
}

Теперь мы смело можем сделать так:

println("Start")
launch(UI) {
   val f5 = factorial(5)
   val f25 = factorial(25)
   myTextView.text = (f5.await() + f25.await()).toString()
}
println("Stop")

Первым делом я проверил, как это дело работает в случае, если контекст умер — работает отлично, утечек я не пронаблюдал! Честно признаться: я и до этого времени AsyncTask совсем не часто использовал, а теперь не вижу необходимости вообще.

Отдельным пунктом хочу заметить, что с применением корутин отпадает необходимость использования колбеков для асинхронных вызовов. Я на дух не переношу ситуации, когда у меня флоу состоит из нескольких последовательных API-коллов. Эти громоздкие конструкции/ветвления переходящие одно в другое — бррр. Теперь же мы просто можем закинуть пачку API-коллов в нужной нам последовательности в корутину, обернуть их, к примеру, в try/catch (чтобы отлавливать различные исключения) и voilà, все готово! И, если я еще не сильно надоел, то хочу подкинуть еще чуток на корутинный вентилятор:

//fetching data for list/recyclerview
launch(UI) {
   try {
       val call = MyHttpClient.getLatestItems() //some call with async() inside
       itemsAdapter.setData(call.await())
       itemsAdapter.notifyDataSetChanged()
    } catch (exc : CustomException) {
           //logging exception 
    }
}

//fetching and processing images
launch(UI) {
   try {
       val imageCall1 = MyHttpClient.fetchImageByUrl(url1)
       val imageCall2 = MyHttpClient.fetchImageByUrl(url2)
       val resultImageFun = combineImages(imageCall1.await(), imageCall2.await()) //also an async() function
       imageView.setImageBitmap(resultImageFun.await())
    } catch (exc : CustomException) {
           //logging exception 
    }
}

Я специально остановился только на двух механизмах работы с корутинами, там их больше, и даже питонисты найдут свой любимый yield.;)

Пытливый ум может упрекнуть меня, что корутины не совсем тянут на базовые вещи в Kotlin, и отвести бы им место в следующей статье рядом с лямбдами, функциями и прочей вкусняшкой....и где-то я с ними соглашусь. Но уж больно хороши они мне показались, и я не смог не поделиться этой информацией с вами!;) Слишком уж сильно они упрощают жизнь и сокращают время от точки А (создание proof of concept) до точки Б (получение готовых результатов), а это дорогого стоит, не так ли?

Лично для себя я сделал уже выбор, и, боюсь, он не сильно в пользу Java. Так что можно дальше баллы не считать. Но о том, ЧТО еще Kotlin умеет, я постараюсь вменяемо рассказать в следующей (последней) части. А теперь можно и на боковую...и только бы не Йода мне приснился, только бы не эта зеленая марты...

просто чел| Трушная оппозиция. Кручу верчу наебать хочу.

2019 Окт.. 20, 18:22 [#60223]
Ну что же, настало время погрузиться в самые интересные разделы документации. Базовый синтаксис, и не только, был озвучен в предыдущей статье, а сейчас настало время пройтись по «функционалу». В свое жалкое оправдание могу сказать, что до встречи с Kotlin не особенно следил за трендом Functional Programming (FP).

Анонимные функции с удовольствием использовал в JS, но это было слишком давно, и к Android-разработке, к сожалению, не применимо. У разработчиков сервер-сайда есть выбор, например, Scala или Groovy, а у Android-разработчиков — его нет. Да, знаю о том, что в Java 8 появились первые наработки по FP, но это все применимо больше к сервер-сайду и под Android этого еще очень долго не будет в нормальном виде! (Ах, как хорошо-то строка ложилась пока Гугль не решил нативно поддерживать Java 8 в Android). Но это не означает, что Kotlin потерял свою актуальность, так ведь?! ;)

Поэтому как-то не было необходимости в это дело погружаться. Да, мне стыдно, нельзя так надолго отставать от тренда, обещаю, что такого больше не повторится (вот уже по горячим следам восстанавливаю свои познания в Python и JS (в него с 2011 года не глядел)). Итак, от слов к делу...

Functions

Когда постоянно варишься в объектно-ориентированном коде, забываешь о тех удобствах, которые предоставляет FP, а о многих можешь просто и не знать! Взять к примеру Local Functions. Бывали у вас случаи, когда вы сделали Extract Method куска кода, но совершенно не хочется его выносить в отдельный метод класса, потому что он используется только в одном каком-то специфическом месте, но несколько раз? Меня всегда это расстраивает/раздражает, ведь в том же Pascal(простите мне мою навязчивую ностальгическую сентиментальность) можно было объявлять локальные (nested) функции/процедуры. И вот, пожалуйста, Kotlin предлагает такую возможность — круто! Более того, локальные функции имеют доступ к переменным объявленным во внешнем контексте. Дважды круто!

Как давно вы использовали TextUtils.isEmpty()? Для тех, кто не в теме, она делает две вещи:

    проверяет переданную строку на null
    проверяет длину строки

Меня всегда раздражало, что я должен использовать для этих двух простых действий сторонний класс и не могу этого сделать с помощью myStringVariable.isEmpty(). Ведь я получу NPE, если в myStringVariable будет null.

Все эти лишние телодвижения не делают код красивее. Как эта проблема решается в Kotlin? Очень просто — Extension Functions, с помощью них можно расширять уже существующие классы без необходимости наследования от них. Да, да, можно взять любой класс из Android SDK (и не только) и расширить его своим набором методов. Вот это крутотень! Я не слишком часто говорю слово «круто»? Я сразу бы пошел расширять класс String и добавлять в него правильный метод isEmpty(), вот только он уже там есть (называется isNullOrEmpty()) и работает как надо, а реализация null safety в Kotlin защитит нас от NPE.

Разберемся, как это дело работает. К примеру, вы всегда хотели знать какой длины окружность получилась бы для произвольного целого числа будь оно радиусом этой окружности. В Java-мире я бы создал класс Circle, к примеру, и метод в нем getLength(), или статический класс с отдельным методом. Некрасиво и громоздко. А теперь посмотрим, как это правильно делается в мире Kotlin:

fun Int?.circleLength() = if (this != null) 2 * this * PI else 0;

println(10.circleLength()) // prints 62.83185307179586
println(null.circleLength()) // prints 0

Не хотите заморачиваться с nullable stuff — убираете ? и проверки на null. Красиво, просто, быстро. Или, например, всегда хотели быстро посчитать кол-во пробелов в строке не прибегая к ухищрениям? Опять опоздали — такая функция уже есть String.count({lambda}).

А как насчет функций, которые синтаксически выглядят как операции? Тоже не проблема, нужно в описание функции в начало добавить ключевое слово infix.

infix fun String.mix() = {...}

"Hello " mix "World"
"Hello ".mix("World")

А вот пример из повседневной жизни на Android:

//this is how we inflate layout to view
LayoutInflater inflater = LayoutInflater.from(getContext());
view = inflater.inflate(R.layout.item_user, container, false);

//and this is how we can do it in a smarter way
fun ViewGroup.inflate(layoutId: Int, attachToRoot: Boolean = false): View {
    return LayoutInflater.from(context).inflate(layoutId, this, attachToRoot)
}
view = viewGroup.inflate(R.layout.item_user, false)

И эту функцию можно использовать во всем проекте! Добавим к этому, что функции можно просто описывать в файле и не обязательно они должны быть внутри какого-то класса. Скажем «До свидания» *Utils-классам — теперь все вспомогательные функции можно хранить вместе! Хотя это уже больше дело вкуса.
Even More Functions

Вернемся в са-а-а-мое начало моего повествования. Кажется я там писал нуднейшую функцию обхода массива с выборкой нужных элементов, удовлетворяющих какому-то условию. Да, это задачка совершенно тривиальная, и такие вот задачи приходится делать достаточно часто, в результате чего классы толстеют, читабельность кода снижается, и вообще тратится куча лишнего времени на создание структуры вместо бизнес-логики. Создаются helper-классы с однотипными методами, где разница лишь в условии выборки. Если вам надоело писать однотипный ugly код, тогда Kotlin идет к вам!

На ваш выбор предлагаются:

    функции высшего порядка (higher-order functions(HOF))
    анонимные функции (anonymous functions)
    лямбды (lambdas)

Все они тесно связаны между собой и по сути нельзя использовать HOF не используя lambdas или anonymous functions.

Для тех, кто не знаком с матчастью: HOF позволяют принимать другие функции в качестве параметров или возвращать функции как результат работы. JS-разработчики пользуются этими вещами практически каждый день, когда делают AJAX-запрос, и в качестве success/error колбеков передают анонимные функции. В Python это тоже реализовано с незапамятных времен. Это удобно и просто. Теперь и под Андроид можно так делать!

А если у вас уже есть готовая функция и вы хотите ее передать в качестве параметра, это тоже запросто можно сделать, поставив ‘::’ перед ее именем. Более того, в Kotlin 1.1 добавили возможность передачи методов экземпляра класса таким же образом! Разберем простой пример, посмотрим, как это работает. Задача следующая: есть список строк, нужно из них выбрать только те, длина которых четная. Нам тут поможет уже знакомая с прошлой статьи функция расширения — isEven().

fun Int?.isEven() : Boolean = this?.rem(2) == 0 //in Kotlin 1.1 mod got deprecated and you need to use rem instead

//just a dummy func used for an experiment
fun check(str : String) : Boolean = str.length.isEven()
//Java-style implementation, just to show how you can supply a function into a function! OMG, what am I saying?!!!
//@param validator - it’s a function that takes String param and returns Boolean value
fun sortOutStrings(list : List<String>, validator : (String) -> Boolean) : List<String> {
    val result = arrayListOf<String>()
    for (item in list)
       if (validator(item))
          result.add(item)
    return result
}

//sending reference of our check() function inside sortOutStrings()
println(sortOutStrings(listOf("A", "AB", "ABC"), ::check)) //prints [AB]

Подобным образом работает функция filter для Collections — принимает на вход функцию/лямбду, результатом которой будет Boolean, чтобы выбрать искомые данные и вернуть коллекцию с ними.

На самом деле данный пример выглядит все еще громоздко, попробуем упростить с помощью анонимной функции и лямбды:

//anonymous function
println(sortOutStrings(listOf("A", "AB", "ABC"), fun(str) = str.length.isEven())) //prints [AB]

//lambda with the chain of other functions that process the result of each other
println(listOf("abc", "ab", "a").filter{!it.length.isEven()}.sortBy{it}.map{it.toUpperCase})//prints [A, ABC]
//lambdas
println(sortOutStrings(listOf("A", "AB", "ABC"), {it.length.isEven()})) //prints [AB]
println(sortOutStrings(listOf("A", "AB", "ABC")){it.length.isEven()}) //prints [AB]

Последние две строчки особо интересны — по сути это одна и та же структура, вот только синтаксис немного отличается. it — неявное имя единственного параметра (а в Kotlin 1.1 в лямбдах можно делать деструктуризацию (destructure) параметра!), таким образом упрощается обращение к параметру в лямбде. Eсли последним параметром в вызове идет функция, то ее тело можно описать вне круглых скобок, а сразу за ними в фигурных. И вот почему это здорово: таким образом можно описывать блоки, состоящие из нескольких функций, и реализовывается это с помощью function literal with a specified receiver object. С одной стороны это очень похоже на функции расширения, потому что мы можем вызывать методы этого объекта без каких-либо дополнительных qualifiers, только теперь еще можно передать набор методов этого объекта, которые мы хотим вызвать. Возьмем пример сверху с view inflation и изменим его, заточив под использование с TextView:

inline fun textview(parent: ViewGroup, setup: TextView.() -> Unit) : TextView {
   val view = TextView(parent.context)
   view.setup()
   parent.addView(view)
   return view
}

Функция textview() принимает последним параметром лямбду setup с явно указанным типом объекта-приемника (receiver object) — TextView. Это можно использовать вот так:

textview(container) {
   layoutParams = LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT)
   setText(R.string.app_name)// OR text = getString(R.string.app_name)
   setTextColor(Color.RED)
   textSize = 12f;
   setOnClickListener { Toast.makeText(context, "That's Me!", Toast.LENGTH_LONG).show() }
}

Т.е. весь код, который находится внутри скобок будет выполнен в том месте, где происходит вызов view.setup(). Симпатично, не правда ли? Думаю, вы уже догадались, где можно и нужно использовать данный подход, правильно — в билдерах & древовидных структурах.

В описании функции textview() присутствует ключевое слово inline. Оно говорит компилятору о том, что код данной функции (т.е. ее тело) нужно вставить непосредственно в место вызова этой функции в чистом виде. Таким образом мы экономим память (т.к. не создается еще одна абстракция), не теряем в быстродействии, НО немного растет количество кода на выходе. Об этом не нужно забывать и по возможности использовать inline максимально и с пользой. К примеру, extension functions для базовых типов реализованы именно так.

Совсем недавно у нас на проекте пришлось рефакторить код, который отвечал за создание, отображение и колбеки диалогов. Вернее как, рефакторить — этого кода вообще не было, а большинство диалогов были сами по себе, хотя по сути выполняли одну и ту же задачу: получали какой-то инпут от пользователя и результат работы возвращали в receiver — вот только каждый делал это по-своему. А тут как раз technical debt подкрался — ну как такой возможностью не воспользоваться? Базовый набор диалогов был такой:

    обычные информационные (с тайтлами и без, с сообщениями и без);
    с подтверждениями(confirmations);
    с единичным выбором (single-choice);
    с множественным выбором (multi-select);
    с полями для firstname/lastname/nickname.

В итоге получилась типичная фабрика на Java, но совершенно не гибкая и со скрепами;). Возьмем, к примеру, процесс создания диалога в Android:

    создаем инстанс нашего DialogFragment класса
    создаем Bundle для аргументов
    наполняем его(bundle) значениями
    отправляем его(bundle) в диалог
    устанавливаем listeners
    отображаем диалог

И так каждый раз, когда нужно показать диалог. Да, это можно и нужно обернуть в метод и это будет выглядеть как-то так:

static public void showSimpleDialog(FragmentManager fManager, String title, String message) {
    MyDialog dialog = new MyDialog();
    Bundle args = new Bundle();
    args.putString(ARG_TITLE, title);
    args.putString(ARG_MESSAGE, message);
    dialog.setArguments(args);
    ...//some other dialog methods calls, i.e. callbacks for the buttons
    dialog.show(fManager, MyDialog.class.getSimpleName());
}

Но есть тут одно НО: как только у нас меняется кол-во аргументов, передаваемых в диалог (дизайнеры ж захотят для некоторых из них, к примеру, кастомные текстовки для кнопок влепить), приходится либо рефакторить этот метод, либо создавать новый. А если еще набор этих аргументов может варьироваться, а именно так у нас на проекте и было, то становится совсем грустно. А теперь посмотрим, как можно решить подобную задачу на Kotlin в первом приближении:

//inline function with receiver object to wrap dialog creation and showing
inline fun mydialog(fragManager: FragmentManager, args: Bundle, init: MyDialog.() -> Unit) : Unit {
   val dialog = MyDialog();
   dialog.arguments = args
   dialog.init()
   dialog.show(fragManager, dialog.javaClass.simpleName)
}

//inline function with receiver object that helps to wrap Bundle initialisation
inline fun bundle(init: Bundle.() -> Unit) : Bundle {
   val result = Bundle()
   result.init()
   return result
}

//this is how we create and show our dialog
mydialog(fragmentManager,
    bundle {
        putString(ARG_TITLE, "Hello?")
        putString(ARG_MESSAGE, "Is There Anybody In There?")
        //here we may put as many params as we want
        …
    }) {

    ...//some other dialog methods calls, i.e. callbacks for the buttons
}

Выглядит фантастически не так ли? Фантастически просто, практично и красиво. И не нужно писать монстро-классы, ведь все можно решить двумя функциями. Если вас зацепило так же как и меня, то советую посмотреть в документации отдельный раздел по Type-Safe Builders, там приведен отличнейший пример построения HTML-разметки, используя подход function with receiver object — настоятельно рекомендую к ознакомлению, фанатам Groovy он точно придется по вкусу.
Something borrowed, Something new in Kotlin 1.1

К набору функций для работы с коллекциями добавился такой замечательный метод как groupingBy(), который позволяет выполнять группировки коллекций и потом их обрабатывать. Такая штука может запросто пригодиться, если вы не хотите (ну да, просто лениво) хранить данные с сервера в локальной базе, но нужно их группировать и делать какие-то агрегации.

Еще добавились такие функции общего назначения как takeIf()/takeUnless(). takeif() подобна filter(), но работает с единичным значением, а не с коллекцией. Она проверяет получателя на соответствие условию, и если все ОК, то возвращает его, иначе — null. А функция takeUnless() работает наоборот. В сочетании с elvis-оператором, можно делать подобные конструкции:

val dummyVal = listOf("a", "ab", "abc", "abcd").get(2).takeIf { it.length.isEven() } ?: "Sorry!"
println(dummyVal)//prints “Sorry!”

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

//very useful extension function ;)
fun String.containsC() : Boolean = this.contains("C", true)

//and here we send this function into takeIf()
val dummyVal = listOf("a", "ab", "abc", "abcd").get(2).takeIf (String::containsC) ?: "Sorry!"
println(dummyVal)//prints “abc”

Выше я уже не удержался и упомянул про деструктуризацию в лямбдах, что само по себе уже здорово. Но в них еще добавили возможность пропуска ненужных параметров с помощью подчеркивания «_».

//this is our distracted data class
data class DistractedDataClass(val id:Int, val text:String, val weight:Int)

val threshold = 6;
val dummyList = listOf(DistractedDataClass(0, "Hello", 1), DistractedDataClass(1, "World", 2))
val goodList = dummyList.filter { (_, txt, w) -> txt.length + w > threshold}
if (goodList.size > 0) println("${goodList.get(0).toString()}")//prints “DistractedDataClass(id=1, text=World, weight=2)”

Coming back to DAY 1

А теперь вернемся к первому абзацу из «DAY 1» и попробуем переработать Java-решение в красивое Kotlin-решение. Я приведу маленький кусочек кода, и его будет достаточно для сравнения:

//Java code
public class Message {
        private String text;
        private boolean isRead;
        private String author;

        public Message(String text, boolean isRead, String author) {
            this.text = text;
            this.isRead = isRead;
            this.author = author;
        }

        public String getText() {return text;}

        public boolean isRead() {return isRead;}

        public String getAuthor() {return author;}
    }

//
public class MessageHelper {
   
    //getting read messages
    static public List<Message> getReadMessages(List<Message> messages) {
        List<Message> result = new ArrayList<>();
        for(Message message : messages) {
            if (message.isRead()) {
                result.add(message);
            }
        }
        return result;
    }
   
    //getting messages by specific author
    static public List<Message> getMessagesByAuthor(List<Message> messages, String author) {
        List<Message> result = new ArrayList<>();
        for(Message message : messages) {
            if (!TextUtils.isEmpty(message.getAuthor()) && message.getAuthor().equals(author)) {
                result.add(message);
            }
        }
        return result;
    }
}

//usage
MessageHelper.getReadMessages(messages);
MessageHelper.getMessagesByAuthor(messages, "Roger");

------
// Kotlin code
data class Message(val text: String, val isRead: Boolean, val author: String?)

//usage
messages.filter { it.isRead }
messages.filter { it.author == "Roger" }

Лично мне больше нечего добавить, кроме как сделать
Заключение

Kotlin — отличная альтернатива Java как для сервер-сайд разработки, так и для Android. Непосредственно для Андроид-разработчиков я бы рекомендовал свой следующий проект писать именно на нем потому что:

    Java под Android все еще хватается за соломинки 6/7-ой версий, и 8-ка только на полпути к нам Восьмерочку завезли и это здорово, но все-таки она не такая вкусная как Kotlin, IMHO;
    Kotlin на 100% совместим с Java: можно перемешивать код Java и Kotlin вместе, использовать классы Kotlin в Java-коде и наоборот + опять-таки Kotlin 1.1 поддерживает Java 8;
    ООП в Kotlin содержит дофига вкусняшек, которые я описал в первой статье (+ в версии 1.1 там еще добавили массу всего);
    Null safety реализована на уровне системы типов;
    вы сможете использовать приемы функционального программирования (higher-order functions, lambdas, anonymous functions, function references) практически без потери производительности (во всяком случае нам так говорят JetBrains) И, как я понимаю, теперь не нужно использовать RetroLambda;
    Kotlin умеет в умные преобразования типов(smart casts);
    Kotlin умеет в функции расширения(extension functions);
    можно использовать Android extension plugin — теперь вам не нужен ButterKnife;
    вы будете создавать меньше кода, чтобы получить результат, и на выходе вы получите лаконичный хорошо читаемый код;
    вы хотите создавать бизнес-логику, а не инфраструктуру для нее;
    теперь вы сможете подумать о проблеме/задаче с другой стороны;
    вы сыты по горло классами-помощниками и этими бесконечными статическими методами;
    вам всегда было интересно, что это за штука такая «лямбда», и почему все так от них торчат тащатся, но не были уверены на пуркуа вам это надо;
    вы хотите выучить что-то новое и интересное, но при этом желательно, чтобы потраченные усилия того стоили;
    вы можете просить повышения ЗП, так как теперь вы знаете еще один язык программирования!
    вы просто....а придумайте-ка за меня еще парочку пунктов;).

hommius| Истинный фанат Дабуры

2019 Окт.. 20, 18:22 [#60224]
Это что-то новенькое  :pepe_clown

просто чел| Трушная оппозиция. Кручу верчу наебать хочу.

2019 Окт.. 20, 18:22 [#60225]
 :snide

Монс| RETURN TO MONSE

2019 Окт.. 20, 18:25 [#60226]
Это что-то новенькое  :pepe_clown
Режим форсажа  :dalton

Уткер| Супермодератор

2019 Окт.. 20, 18:28 [#60227]
 psy_duck

просто чел| Трушная оппозиция. Кручу верчу наебать хочу.


Бьерн| Хуй сосешь?

2019 Окт.. 20, 18:48 [#60229]
Это чо блядь?  :snide

Caesar Klaun| Величайший ученый

2019 Окт.. 20, 19:00 [#60230]
Чел, бля! :angry_pepe
В оффтоп подобную хуйню кидай :ohunyanno

Попсик| They/them

2019 Окт.. 20, 19:04 [#60231]
-чизик-пызик где-ты был?
-снова в прудике глупил :pepe_clown :pepe_clown :pepe_clown
 

Caesar Klaun| Величайший ученый

2019 Окт.. 20, 19:06 [#60232]
-чизик-пызик где-ты был?
-снова в прудике глупил :pepe_clown :pepe_clown :pepe_clown
Глупышку попса тралировал :dalton

Pryn| Потужный пряник

2019 Окт.. 20, 19:06 [#60233]
А какая любимая?
 :love
Парсоул конечно, но и Валентайн и Сквигли со скином "лав и крафт" просто бимба :antonio

Rindo| It doesn't matter

2019 Окт.. 20, 19:11 [#60234]
ну ты и натурал женек, пиздец конечно, мда :pepe_clown

Монс| RETURN TO MONSE

2019 Окт.. 20, 19:24 [#60235]
Парсоул конечно, но и Валентайн и Сквигли со скином "лав и крафт" просто бимба :antonio
Ну кстать мой топ тян это Филия  :pepe_clown

Монс| RETURN TO MONSE

2019 Окт.. 20, 19:26 [#60236]
Насыпали крошек за это сообщение: One Piece
Риндо до сих пор на меня в обидках за то, что разнес его в теме башни.

Сук, ну я же спецом соврал бороде что ты могуч, мда.

 :dalton

Mid| ZZZehahahaahaha

2019 Окт.. 20, 19:26 [#60237]
:WAT
Все норм, ты большой инструмент :black_overlord

Монс| RETURN TO MONSE

2019 Окт.. 20, 19:28 [#60238]
Риндо до сих пор на меня в обидках за то, что разнес его в теме башни.

Сук, ну я же спецом соврал бороде что ты могуч, мда.

 :dalton
Бло, ппц, под каждым сообщением, вот это обида, не ожидал от тебя такого Риндос, да ты походу непидор совсем :antonio

Леопольд| Игроки

2019 Окт.. 20, 19:32 [#60239]
Парсоул конечно, но и Валентайн и Сквигли со скином "лав и крафт" просто бимба :antonio
О, да.
 :antonio
Как же у нас с тобой вкусы похожи.