Весь март официальный твиттер-аккаунт Google, посвященный разработке приложений для Android (@AndroidDev), публиковал короткие, но очень полезные заметки о разработке на языке Kotlin.
Рекомендуем: Inline-функции Kotlin
Советы о разработке на Kotlin
Марк Гарсия (Marc Garcia) собрал их все вместе в одной статье. Привожу максимально краткую, но понятную выжимку.
- Элвис-оператор (если null, то…):
12[crayon-6759ab924ab80412106230 inline="true" ]val name: String = person.name ?: "unknown" - Строковые шаблоны:
12[crayon-6759ab924ab82567638619 inline="true" ]val text = "$language has ${language.length} characters" - Разрушающие объявления:
123[crayon-6759ab924ab84748732923 inline="true" ]val (red, green, blue) = colorval (x, y) = point - Оператор when:
12345678[crayon-6759ab924ab86590025238 inline="true" ]return when (cargo) {null, 0 → "empty"1 → "tiny"2..10 → "small"is Int → "big inty"else → "$cargo"} - Цикл for:
123456[crayon-6759ab924ab88819092616 inline="true" ]for(i in 1..100) { /* ... */ }for(i in 100 downTo 1) { /* ... */ }for(i in 1 until array.size step 2) { /* ... */ }for((index, element) in array.withIndex()) { /* ... */ }for((key, value) in map) { /* ... */ } - Свойства и поля:
12345678910111213[crayon-6759ab924ab8a915430939 inline="true" ]class User {val id: String = "" // Неизменяемое полеvar name: String = "" // Изменяемое полеvar surname: String = "" // Изменяемое поле с кастомным геттеромget() = surname.toUpperCase()var email: String = "" // Изменяемое поле с кастомным сеттеромset(value) {if(isEmailValid()) field = value}} - Классы данных (Kotlin автоматически добавляет к ним методы equals(), toString() и copy()):
12[crayon-6759ab924ab8c416583552 inline="true" ]data class User(val name: String, val email: String /* ... */ ) - Модификаторы видимости:
1234[crayon-6759ab924ab8e694647760 inline="true" ]val isVisible = trueprivate val isHidden = trueinternal val almostVisible = true - Аргументы по умолчанию:
12345678910[crayon-6759ab924ab90087764458 inline="true" ]class BulletPointSpan(private val bulletRadius: Float = DEFAULT_BULLET_RADIUS,private val gapWith: Int = DEFAULT_GAP_WIDTH,private val color: Int = Color.BLACK) { /* ... */ }val bulletPointSpan = BulletPointSpan()val bulletPointSpan2 = BulletPointSpan(resources.getDimension(R.dimen.radius))val bulletPointSpan3 = BulletPointSpan(color = Color.RED) - Изолированные классы (по сути прокачанный enum):
1234567891011121314[crayon-6759ab924ab92498791367 inline="true" ]sealed class NetworkResultdata class Success(val result: String): NetworkResult()data class Failure(val error: Error): NetworkResult()// one observer for success and failureviewModel.data.observe(this,Observe<NetworkResult> { data ->data ?: return@Observerwhen (data) {is Success → showResult(data.result)is Failure → showError(data.error)}}) - Ленивая инициализация (переменная инициализируется в момент первого доступа):
12[crayon-6759ab924ab94662128552 inline="true" ]val preferences: String by lazy { sharedPreferences.getString(PREFERENCE_KEY) } - Инициализируемые позже не null-переменные:
1234[crayon-6759ab924ab96354448215 inline="true" ]lateinit var recyclerView: RecyclerView...recyclerView = findViewById(R.id.recycler_view) - Проверка аргументов (выбрасывает IllegalArgumentException, если условие не соблюдено):
12[crayon-6759ab924ab98761925840 inline="true" ]require(name.isNotEmpty()) { "Invalid name" } - Инлайн-функции (их тело будет встроено в код вместо вызова):
1234[crayon-6759ab924ab9a068207339 inline="true" ]inline fun onlyIf(check: Boolean, operator: () → Unit) {if (check) { operation() }} - Вызов функций, объявленных из класса с помощью Java:
123456789[crayon-6759ab924ab9c041798661 inline="true" ]@file:JvmName("ShapeGenerator")package com.shapesfun generateSquare() = Square()fun generateTriangle() = Triangle()// Вызов из JavaShapesGenerator.generateSquare() - Параметры вещественного типа:
123456789[crayon-6759ab924ab9e361891733 inline="true" ]// Обычно мы делаем такval alarmManager = context.getSystemService(AlarmManager::class.java)// Но можем упростить этот код до такогоval alarmManager: AlarmManager = context.systemService()// Для этого достаточно такой функцииinline fun <refied T> Context.systemService() = getSystemService(T::class.java) - Делегирование (передача ответственности за хранение состояния переменной другому коду):
12345678910111213[crayon-6759ab924aba0063293840 inline="true" ]class MyAnimationView : View( /* ... */ ) {// Delegated property. Uses the getter and setter defined in InvalidateDelegatevar foregroundX by InvalidateDelegate(0f)}class InvalidateDelegate<T : Any>(var value: T) {operator fun getValue(thisRef: View, property: KProperty<*>) = valueoperator fun setValue(thisRef: View, property: KProperty<*>, value: T) {this.value = valuethisRef.postInvalidateOnAnimation()}} - Функции расширения (позволяют добавить свой метод в любой класс):
123[crayon-6759ab924aba2846283635 inline="true" ]inline fun String.toUri(): Uri = Uri.parse(this)val myUri = "www.developer.android.com".toUri() - Конвертирование Drawable в Bitmap (с помощью библиотеки Android KTX):
123[crayon-6759ab924aba5481556529 inline="true" ]val myDrawable = ContextCompat.getDrawable(context, R.drawable.icon)val bitmap = myDrawable.toBitmap() - Последовательности:
123456[crayon-6759ab924aba7806526048 inline="true" ]val sequence = List(50= { it * 5}.asSequence()sequence.map { it * 2 }.filter { it % 3 == 0 }.map { it + 1 }.toList() - Перегрузка операторов:
1234567[crayon-6759ab924aba9375191932 inline="true" ]inline operator fun Spannable.plusAssign(span: Any) =setSpan(span, 0, length, SPAN_INCLUSIVE_EXCLUSIVE)val spannable = "Eureka!!!!".toSpannable()spannable += StyleSpan(BOLD)spannable += UnderlineSpan() - Функции без классов:
12345[crayon-6759ab924abab745914765 inline="true" ]@BindingAdapter("userItems")fun userItems(recyclerView: RecycleView, list: List<User>?) {...} - Итераторы библиотеки Android KTX (позволяют проходить в цикле, например, по ViewGroup и SparseArray):
123[crayon-6759ab924abad553604535 inline="true" ]for (view in ViewGroup) { }for (key in spaceArray.keyIterator()) { } - Упрощенная работа с Content Values с помощью Android KTX:
1234567[crayon-6759ab924abaf165466812 inline="true" ]val contentValues = contentValuesOf("KEY_INT" to 1,"KEY_LONG" to 2L,"KEY_BOOLEAN" to true,"KEY_NULL" to null) - DSL (Kotlin позволяет создавать «языки в языке» для специальных нужд):
12345678[crayon-6759ab924abb1177134254 inline="true" ]frameLayout {button("Light a fire") {onClick {lightAFire()}}} - Упрощенная работа с бандлами с помощью Android KTX:
12345678[crayon-6759ab924abb3525441436 inline="true" ]val bundle = bundleOf("KEY_INT" to 1,"KEY_LONG" to 2L,"KEY_BOOLEAN" to true,"KEY_NULL" to null,"KEY_ARRAY" to arrayOf(1, 2)} - Лямбды:
123456[crayon-6759ab924abb5163959563 inline="true" ]fun Handler.postDelay(delay: Int /* ... */, action: () → Unit)handler.postDelay(50) {...} - Упрощение работы со Spannable с помощью Android KTX:
1234567891011[crayon-6759ab924abb7203411063 inline="true" ]val string = buildSpannableString {append("no styling text")bold {append("bold")italic { append("bold and italic") }}inSpans(RelativeSizeSpan(2f), QuoteSpan()) {append("double sized quote text")}} - Библиотека Parcelize:
123[crayon-6759ab924abb9867051610 inline="true" ]@Parcelabledata class User(val name: String, val occupation: Work): Parcelable - Расширенный Android API с помощью Android KTX:
12345[crayon-6759ab924abbb720003568 inline="true" ]view.updatePadding(left = newPadding)view.updatePadding(top = newPadding)view.updatePadding(right = newPadding)view.updatePadding(bottom = newPadding) - Функции let, apply, with, also и run:
12345[crayon-6759ab924abbd971240873 inline="true" ]val string = "a"val result = string.apply {// Этот код будет выполнен в контексте объекта string}