Post on 12-Apr-2017
KotlinКогда уже закончиться какой-то год 2006* ?
* год выхода Java SE 6
Какова жизнь с Java 6?
• Нельзя добавить методы для типов, определенных платформой либо сторонней библиотекой
• Распространенность java.lang.NullPointerException
• Android поддерживает только Java 6 ** есть небольшой функционал от Java 7
• Нет Stream API для коллекций
Что вы получите при использовании Kotlin?
• Полная интероперабельность с Java• Null-Safety• Ссылки на методы, замыкания (closure)• Лямбды из коробки• Немодифицируемые коллекции
Переменные
• val sample: String = "Sample String”• var sample = "Sample String"
var sample: List<String> = ArrayList()
NULL• В Kotlin необходимо явно указывать - может ли
переменная принимать null
• Nullable переменные должны быть промаркированы знаком вопрос “?” после типа, т.е.:
val name : String? = null
• Можно присвоить значение от not-nullable типа в nullable без каки либо проблем :
fun main(args: Array<String>) { val firstName: String = "Adam" // not nullable val name : String? = firstName // nullable}
• В противном случае необходимо явно декларировать, что nullable переменная не содержит null. Это достигается с помощью оператора двойного восклицания “!!” (который указывает: “Я точно уверен, что эта переменная не содержит null”)
fun main(args: Array<String>) { val name: String? = "Adam" val firstName: String = name!! print("$firstName")}
Классы
public class Student {
private int course; private String name; private final Date birthday;
public Student(int course, @NotNull String name, @NotNull Date birthday) { this.course = course; this.name = name; this.birthday = birthday; }
public int getCourse() { return course; } public void setCourse(int course) { this.course = course; } @NotNull public String getName() { return name; } public void setName(@NotNull String name) { this.name = name; } @NotNull public Date getBirthday() { return birthday; }
@Override public boolean equals(Object o) { if (this == o) return true; if (o == null || getClass() != o.getClass()) return false; Student student = (Student) o; return course == student.course && name.equals(student.name) && birthday.equals(student.birthday); } @Override public int hashCode() { int result = course; result = 31 * result + name.hashCode(); result = 31 * result + birthday.hashCode(); return result; }}
class Student(var course: Int, var name: String, val birthday: Date) {
override fun equals(other: Any?): Boolean { if (this === other) return true if (other?.javaClass != javaClass) return false other as Student return course == other.course && name == other.name && birthday != other.birthday }
override fun hashCode(): Int { var result = course result += 31 * result + name.hashCode() result += 31 * result + birthday.hashCode() return result }}
data class Student( var course: Int, var name: String, var birthday: Date)
Generate methods:1. toString2. equals / hashCode3. componentN4. copy
Однострочные ф-ии• Это сокращенная форма определения функции, когда
у вас есть только одно выражение, которое будет выполнено.Необходимо заметить, что нет необходимости явно указывать возвращаемый тип:
fun main(args: Array<String>) { val res = add(1, 1) p(“$res") println(englishGreeting())}
fun englishGreeting() : String = "Hello world"fun add(a: Int, b: Int) = a + bfun isValueMore(value : Int) = if (value > 0) 1 else -1
Порядок аргументов
• Котлин позволяет указывать параметры по-умолчанию, делая их не обязательными:
fun main(args : Array<String>) { greet(firstName = "Frasensco", lastName = "Merini") greet(lastName = "John", firstName = "Stamos") greet("Borat", "Ismail") greet("Crystal", lastName = "Stamos") call("Xavier", age = 20, location = "Portugal")}
fun greet(firstName : String, lastName : String){ println("Good morning $firstName $lastName")}
fun call(name : String, location : String, age : Int){ println("Call $name who lives at $location and he is $age old")}
А теперь реально крутые вещи
Destructuring Declarations
Представляет собой возможность разложить объект на составляющие:
data class Report(val name: String, val date: Long)
fun loadReport(uuid: String) { api.getReport(uuid) { (name, date) -> reportView.text = name dateView.text = formatDate(date) }}
или в Map
for ((key, value) in map) { println("Good morning $value")
}
When• В Java сравнение (if, switch) может работать с : byte, short, char, int.
Также можно использовать Enum и String (начиная с JDK7), и специальные классы, которые являются обёрткой для примитивных типов: Character, Byte, Short, Integer
• В Kotlin можно работать с большим количеством классов, проверять на принадлежность, сравнивать с выражениями, узнавать попадение в диапозон и т.д.:
return when (value) { "color" -> "Color" is String -> "String" is Int -> “Integer" 1 + 2 -> "3" value == 12 -> “twelve" in 1..10 ->"x is in the range" in validNumbers ->"x is valid" !in 10..20 ->"x is outside the range" else -> throw IllegalArgumentException("Invalid param value")}
Lambdasreturn parsedContacts .filter { it.name != null && it.image != null } .sortedBy { it.name } .map { Contact(it.id, it.name!!, it.image!!) }
val positives = list.filter { x -> x > 0 }
или короче
val positives = list.filter { it > 0 }
String Templates
val student = Student(1, "Ivan Ivanov", Date())
println("Student '${student.name}' is on ${student.course} course.")
Delegated propertiesclass Foo { private var text: String? = null
private fun computeText(): String { … }
fun getText(): String { if (text == null) { text = computeText() } return text!! }}
Delegated properties
class Foo { val text: String by lazy { computeText() } private fun computeText(): String { … }}
Delegated properties
class SampleFragment : Fragment() { val margin: Int by lazy { resources.getDimensionPixelSize(R.dimen.std_margin) }}
Callable references*привет Java 8
• Что делать, если у Вас уже есть ф-ия которую вы хотите передать как параметр ? Просто необходимо добавить префикс “::”
fun main(args: Array<String>) { calcAndShow(10, 10, ::add) //20 calcAndShow(10, 10, ::multiply) // 100 calcAndShow(10, 19, { x, y -> x - y }) //-9}
fun calcAndShow(a: Int, b: Int, func: (a: Int, b: Int) -> Int) { val result = func(a, b) println("$result")}
fun add(a: Int, b: Int): Int = a + bfun multiply(a: Int, b: Int): Int = a * b
Extension function• Эти функции предоставляют возможность расширить класс
с новыми функциональными возможностями, не наследуясь от класса или используя любой тип шаблона проектирования, таких как Decorator.
fun Int.show(){ println("This number is $this")}
fun main(args : Array<String>){ 3.show()}
fun <T: Parcelable> T.toJson(): String{ ...}
fun Activity.toast(message: CharSequence, duration: Int = Toast.LENGTH_SHORT) { Toast.makeText(this, message, duration).show();}
fun ImageView.loadUrl(url: String) { Picasso.with(context).load(url).into(this)}
fun test(actvitiy: Activity){ val image = ImageView(actvitiy) image.loadUrl("http://....") actvitiy.toast(“hello")}
Extension function expression
fun SQLiteDatabase.trans(func: SQLiteDatabase.() -> Unit) { beginTransaction() try { func() setTransactionSuccessful() } finally { endTransaction() }}
db.trans { delete("tableName", "firstName =?", arrayOf("Jake"))}
Extension function в infix форме
fun main(args : Array<String>) { val res = 1 add 2 println("$res")}
infix fun Int.add (value : Int) : Int = this + value
• Если extension ф-ия принимает только один аргумент, то вы можете вызвать их в infix форме (убирается "." между типом и функции). Таким образом, вместо 1.add(2), вы можете вызвать его в виде 1 add 2. Это заставляет выглядеть некоторые конструкции более естественно и особенно полезны в строительстве DSL в Котлин.
У Kotlin нет примитивных
типов
Так что там с Android ?
kotterknifepublic class PersonView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { val firstName: TextView by bindView(R.id.first_name) val lastName: TextView by bindView(R.id.last_name)
// Optional binding. val details: TextView? by bindOptionalView(R.id.details)}
https://github.com/JakeWharton/kotterknife
Уже старо !!! Последний update был Aug 19, 2016
kotlinx.android// Using R.layout.activity_main from the main source setimport kotlinx.android.synthetic.main.activity_main.*
class MyActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) textView.setText("Hello, world!") // Instead of findView(R.id.textView) as TextView }}
https://kotlinlang.org/docs/tutorials/android-plugin.html
Ankoclass MyActivity : Activity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) verticalLayout { val name = editText() button("Say Hello") { onClick { toast("Hello, ${name.text}!") } } } }}
https://github.com/Kotlin/anko
Проблемы• Наследование у data class’ов. Обещают в версии 1.1• Lint для Kotlin - мало проверок• Подсветка кода - не такая хорошая как для Java• Incremental compilation - пока не работает стабильно• С непривычки надо не забывать что листы нельзя
изменять (они не мутабильные)• Runtime null checks - если null все таки попался, то
приложение валиться. • Тернарые операторы (x ? y : z) - Выход val max = if (a > b) a
else b
Проблемы• При достаточно сложной архитектуре
приложения необходимо будет сделать некоторый рефаторинг
• Не такое большое комюнити, в отличие от Java и Scala (пока что)
• Документация могла быть и лучше• Проблемы с annotation processing (все меньше и
меньше)• Из-за молодости языка нет каких-то
выведенных best practices для решения конкретных задач
Что нас ждет в Kotlin v1.1
Underscore for unused parameters
data class Person( val name: String, val city: String, val age: Int)
fun printAll(persons: List<Person>) { val children = persons.filter { persons.forEach { (name, _, age) -> println("$name is $age years old")}
Restructuring for lambdas
data class Person( val name: String, val city: String, val age: Int)
fun sayHelloToChildren(persons: List<Person>) { val children = persons.filter { persons -> persons.age < 18 }
Restructuring for lambdas
data class Person( val name: String, val city: String, val age: Int)
fun sayHelloToChildren(persons: List<Person>) { val children = persons.filter { persons -> persons.age < 18 }
children.forEach { (name, city) -> println("Hello $name from $city") }}
Local delegated properties
fun main(args: Array<String>) { val text by lazy { "Hello world!" }
if (Math.random() > 0.5) { println(text) }}
Asynchronous computations
Предположим, что нам надо выполнить следующий код:
loadImage().whenComplete { image -> resize(image).whenComplete { resizedImage -> panel.setImage(resizedImage) }}
https://josephlin55555.files.wordpress.com/2015/12/screen-shot-2015-12-29-at-6-28-45-pm.png
CoroutinesloadImage().whenComplete { panel.setImage(image)}
asyncUI { val image = await(loadImage()) panel.setImage(image)}
Q&A
Спасибо :)
• Try Kotlin online - http://try.kotlinlang.org/• Documentation -
http://kotlinlang.org/docs/reference/