Kotlin Flashcards
Sealed classes
Sealed classes
Sealed classes - абстрактные классы или интерфейсы, которые похожи на Enums (подклассы известныво время компиляции) но sealed подклассы могут иметь множество экземпляров с разными данными.
Главным преумуществом Sealed классов является использование их в when. Нет нужды в else блоке и благодаря смарт кастом, удобно использовать данные
sealed class Response<out R> class Success<R>(val value: R): Response<R>() class Failure(val error: Throwable): Response<Nothing>() fun handle(response: Response<String>) { val text = when (response) { is Success -> "Success, data are: " + response.value is Failure -> "Error" } print(text) }
аналог Enum. Нужно использовать Enum
sealed class PaymentOption { object Cash object Card object Transfer }
What types of delegation kotlin has? There are 2 types
Class and propery delegation
What is it explicit delegation?
This is delegation using OOP (composition) without any specific feature of the language
interface View { fun show() } class ViewImpl(): View { override fun show() { println("ViewImpl show") } } class Screen(private val view: View): View { override fun show() { view.show() } } fun main() { val screen = Screen(ViewImpl()) // establish delegation between two objects screen.show(); //ViewImpl.show() }
What is it implicit delegation?
class automatically delegates method calls to an instance of another class that implements a specific interface. Kotlin has this feature using by word
interface View { fun show() } class ViewImpl(): View { override fun show() { println("ViewImpl show") } } class Screen(private val view: View): View by view fun main() { val screen = Screen(ViewImpl()) // establish delegation between two objects screen.show(); //ViewImpl.show() }
What is it Delegated Properties?
Sometimes getters and setters use the same code in different classes. To reuse this code Delegated Properties were invented.
class SimpleDelegate { operator fun getValue(thisRef: Any, property: KProperty<*>): String { return thisRef::class.java.name } operator fun setValue(thisRef: Any?, property: KProperty<*>, value: String) { println("you pass me $value") } } class Example { var mutable by SimpleDelegate() }
How to create Delegated Property?
It’s possible to write any class that has getValue or setValue method. The best pratic is to implement interfaces ReadOnlyProperty or ReadWriteProperty. You can’t make a mistake implementing these interfaces
Delegation to another property
It’s possible to delegate one property to another by property of the same class, by top-level property or by property of another class
var topLevelInt: Int = 0 class ClassWithDelegate(val anotherClassInt: Int) class MyClass(var memberInt: Int, val anotherClassInstance: ClassWithDelegate) { // проперти самого класса var delegatedToMember: Int by this::memberInt // топ-левел проперти var delegatedToTopLevel: Int by ::topLevelInt // другой класс val delegatedToAnotherClass: Int by anotherClassInstance::anotherClassInt } var MyClass.extDelegated: Int by ::topLevelInt
Example
class MyClass {
var newName: Int = 0
@Deprecated(“Use ‘newName’ instead”, ReplaceWith(“newName”))
var oldName: Int by this::newName
}
fun main() {
val myClass = MyClass()
// Notification: ‘oldName: Int’ is deprecated.
// Use ‘newName’ instead
myClass.oldName = 42
println(myClass.newName) // 42
}
How does groupBy work?
Transform collection to Map by some criteria
// Count the number of users in each city val usersCount: Map<City, Int> = users .groupBy { it.city } .mapValues { (_, users) -> users.size }
Difference between With and Run in Kotlin
Run and With are interchangeable in some cases. For nullable variable it’s better to use run because you don’t need to check for null inside the block
with(webview.settings) {
this?.javaScriptEnabled = true
this?.databaseEnabled = true
}
// similarly
webview.settings?.run {
javaScriptEnabled = true
databaseEnabled = true
}
run - this is extension while with is lambda with receiver
public inline fun <T, R> T.run(block: T.() -> R): R = block() // equivalent to this.block() public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
What does contentEquals do in Kotlin?
contentEquals function is used to check if two arrays have the same content.
fun main() { val array1 = arrayOf(1, 2, 3) val array2 = arrayOf(1, 2, 3) val result = array1.contentEquals(array2) println(result) // true // false if array1.equals(array2) }
Type hierarchy in Kotlin. What is it Any, Unit, Nothing?
Any is the root of the class hierarchy, and all classes inherit from it. Any = Object in Java without finalize, wait and notify methods
Unit = Void in Java. It means that method doesn’t return any value. Unis is a singleton object.
Nothing is the heir of any types. Nothing means that function never completes. For example: throw Exception or TODO
Kotlin has the same type hierarchy for nullable types:
Any? <- Number? <- Nothing?
Secondary constructor in Koltin
Secondary constructor should always call main consturctor
class SqlQuery(sqlDialect: String) { val config = SqlConfig() val dialect = SqlDialect(sqlDialect) constructor() : this(””) { config.hasCaching = false } }
this code create main and secondary constructors. Fields are initialized in main constructor
Interfaces in Kotlin
Interfaces can contain both abstract methods and implementations. The difference from abstract classes is that interfaces can’t have state
interface MyInterface { val prop: Int // abstract val propertyWithImplementation: String get() = "foo" fun foo() { print(prop) } } class Child : MyInterface { override val prop: Int = 29 }
Data classes in Kotlin. Restrictions.
data classes are a special type of class that is primarily used to hold data and provide automatic implementations for commonly used methods: toString(), equals(), hashCode(), copy(), componentN()
Data classes restrictions:
- no open
- no abstract
- no inner
- no sealed
You can inherit a data class from another regular class or an interface. The properties of the parent class will not affect the automatically generated methods of the data class, such as toString(), equals(), hashCode(), copy(), componentN() but only if they are not included in the primary constructor of the child data class.
Destructuring declarations in Kotlin
val (name, age) = person data class Person(val name: String, val age: Int) // under the hood data class Person(val name: String, val age: Int) { operator fun component1() : String = name operator fun component2() : String = age }
Function componentN() should be declared with operator. Data classes create componentN function by default. Destructuring declarations are positional-based, not named-based.
Destructuring declarations works in arrays and lists but have limitatation to 5
val array = arrayListOf<Int>(1,2,3,4,5,6)
val (b1, b2, b3, b4, b5) = list
b6 - will not compile</Int>
Sealed classes in Kotlin
Sealed classes are abstract classes or interfaces that are like enums (subclasses known at compile time), but sealed subclasses can have multiple instances with different data.
The main advantage of sealed classes is their usage in when expressions. There is no need for an else block, and thanks to smart casting, it is convenient to work with the data.
sealed class Response<out R> class Success<R>(val value: R): Response<R>() class Failure(val error: Throwable): Response<Nothing>() fun handle(response: Response<String>) { val text = when (response) { is Success -> "Success, data are: " + response.value is Failure -> "Error" } print(text) }
Operator overloading
plus/minus/time/div
get/set aka []
rangeTo aka ..
contains aka in
compare aka <=>
in android studio it’s possible to write operator fun and you will see the list of hints
infix function
infix function allows to write a foo b
infix fun Int.add(other: Int): Int { return this + other } fun main() { val result = 5 add 3 println(result) // Output: 8 }
@JvmStatic
Create exta method in bytecode to call static methods from Java
Math.abs(-2) // now it’s possible
Math.Companion.abs(-2)
Inline functions
Inline at the bytecode level inserts the body of the function directly into the code
If we have a lambda, we cannot use return inside it; we can only use return with a label. However, if the function is inline and contains a lambda with a return, it will return of the entire function
fun foo() { inlined { return // OK: the lambda is inlined } }
fun hasZeros(ints: List<Int>): Boolean { ints.forEach { if (it == 0) return true // returns from hasZeros because forEach is inline } return false }
An inline function cannot access private data from the outer scope.
noinline function Kotlin
If an inline function has multiple lambda parameters, and we don’t want them to behave as inline, we declare them with the “noinline” keyword
inline fun higherOrderFunction(aLambda: () -> Unit, noinline dontInlineLambda: () -> Unit, aLambda2: () -> Unit) { ... aLambda() dontInlineLambda() //won't be inlined. aLambda2() ... }
crossinline kotlin
Non-local control flow is not allowed. The difference from “noinline” is that the lambda code will be inlined into the calling function, but the “return” statement will be prohibited.
Lambdas
Lambda is a function without the “fun” keyword, and therefore lambdas can be used inside expressions or as parameters.
Full declaration:
val lambda : (String, String) -> String = { first: String, last: String -> "My name is $first $last" }
calling it:
lambda("Hello", "Kotlin") //or lambda.invoke("Hello", "Kotlin")
If we have a lambda, we cannot use the “return” statement inside it; instead, we can only use “return” with a label. The exception is inline functions:
val lambda = greet@ { greeting: String, name: String -> if(greeting.length < 3) return@greet println("$greeting $name") }
Lambdas can access data and functions outside of their scope; they have access to the outer context
The SAM (Single Access Method) concept allows replacing the implementation of an interface using a lambda.
fun interface Greeter {
fun greet(item: String)
}
fun greetLanguages(languages: List<String>, greeter: Greeter) {
languages.forEach { greeter.greet(it) }
}</String>
fun main() {
val languages = listOf(“Kotlin”, “Java”, “Swift”, “Dart”, “Rust”)
greetLanguages(languages) { println("Hello $it") } // otherwise greetLanguages(languages, object : Greeter { override fun greet(item: String) { println("Hello $item") } }) }
Anonymous functions
Function without name
val sum = fun(a: Int, b: Int): Int { return a + b } // Usage val result = sum(3, 5)
ints.filter(fun(item) = item > 0)