Kotlin Flashcards

1
Q

Sealed classes

A

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
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

What types of delegation kotlin has? There are 2 types

A

Class and propery delegation

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

What is it explicit delegation?

A

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()
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

What is it implicit delegation?

A

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()
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

What is it Delegated Properties?

A

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 well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

How to create Delegated Property?

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Delegation to another property

A

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 well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

How does groupBy work?

A

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 }
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Difference between With and Run in Kotlin

A

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()
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

What does contentEquals do in Kotlin?

A

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)
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Type hierarchy in Kotlin. What is it Any, Unit, Nothing?

A

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?

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Secondary constructor in Koltin

A

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

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Interfaces in Kotlin

A

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
}
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Data classes in Kotlin. Restrictions.

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Destructuring declarations in Kotlin

A
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>

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Sealed classes in Kotlin

A

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)
}
17
Q

Operator overloading

A

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

18
Q

infix function

A

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
}
19
Q

@JvmStatic

A

Create exta method in bytecode to call static methods from Java
Math.abs(-2) // now it’s possible
Math.Companion.abs(-2)

20
Q

Inline functions

A

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.

21
Q

noinline function Kotlin

A

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()
    ...
}
22
Q

crossinline kotlin

A

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.

23
Q

Lambdas

A

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")
    }
}) }
24
Q

Anonymous functions

A

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)
25
Higher-order function Kotlin
higher-order functions are functions that can take other functions as parameters or return functions as results.
26
Sequence
They are executed lazily and do not create intermediate results. Unlike Iterable, the operators are applied not to the entire collection one by one but immediately to the first element, then to the second, and so on
29
Contravariance
Contravariance suggests that if we have classes Base and Derived, where Base is a base class for Derived, then we can assign a value of SomeClass to an object of SomeClass. open class Animal class Dog : Animal() interface Processor { fun process(item: T) } class AnimalProcessor : Processor { override fun process(animal: Animal) { animal.eat() } } fun main() { val animalProcessor: Processor = AnimalProcessor() val dog = Dog() animalProcessor.process(dog) } ``` fun addAnimalsToShelter(shelter: MutableList) { shelter.add(Dog("Buddy")) // Safe to add a Dog shelter.add(Dog("Rover")) // Safe to add another Dog // shelter.add(Cat("Whiskers")) // ERROR: Can't add Cat because it's not a Dog or supertype println("Animals added to shelter.") } fun main() { val animalShelter: MutableList = mutableListOf() val dogShelter: MutableList = mutableListOf() // You can pass a MutableList because Animal is a supertype of Dog addAnimalsToShelter(animalShelter) // You can also pass a MutableList directly addAnimalsToShelter(dogShelter) } ``` The in keyword makes the list contravariant, meaning you can pass a list of Dog or any of its supertypes (e.g., Animal). You can safely add Dog objects to the list, regardless of whether it's a MutableList or a MutableList. Write-Only (Input)
30
Type projection Kotlin
Sometimes we can't use in or out for the class. For example Array - we need to write and read but we can restrict it in local places ``` fun copy(from: Array, to: Array) { ... } ```
31
Star-projection
Star-projection in Kotlin allows working with generics when the exact type is unknown or irrelevant. Key points: Reading: You can read elements, but they'll be of type Any? since the actual type is unknown. Writing: Generally, you can't write into collections using star-projection due to type uncertainty. fun printValues(boxes: List>) { for (box in boxes) { println(box.value) // Any? } }
32
Reified Kotlin
The reified keyword is used in combination with the inline modifier to enable type information to be available at runtime within a function. It allows you to access and manipulate the type parameters of a generic function within its body. ``` inline fun printType() { println(T::class.simpleName) } fun main() { printType() // Prints: Int printType() // Prints: String } ``` if T is not reified then there would be the a compile error
33
Delegation Kotlin
There are class and property delegations in Kotlin Delefation can be explicit using usual OOP mechanics and impicit using language feature: ``` 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() } ``` and the same using Kotlin delegation: ``` class Screen(private val view: View): View by view fun main() { val screen = Screen(ViewImpl()) // establish delegation between two objects screen.show(); //ViewImpl.show() } ```
34
Delegated Properties
Sometimes getters and setters use the same code in different classes. To reuse the 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() } ``` it is best to implement the ReadOnlyProperty and ReadWriteProperty interfaces not to make mistakes ``` class Delegate : ReadOnlyProperty { override fun getValue(thisRef: Activity, property: KProperty<*>): View { val resourceId = thisRef.resources .getIdentifier(property.name, "id", BuildConfig.APPLICATION_ID) return thisRef.findViewById(resourceId) } } ```
35
Initialization order
1. Default arguments are initialized in the order of constructor invocation, starting from the constructor that was called (e.g., secondary constructor), and then passed to the delegated constructors (e.g., primary constructor), and finally, the default arguments of the parent class constructor are initialized. 2. Initializers (property initializers and init blocks) are executed before the constructor bodies, in the order they are declared in the class, from top to bottom. First, the initializers of the parent class are executed, followed by the initializers of the child class. 3. Constructors are executed after all initializers. If a secondary constructor is called, it first delegates to the primary constructor, which must finish before the secondary constructor's body is executed. The constructors of the parent class are executed first, followed by the constructors of the child class. The constructors and initializers of superclasses are executed first, ensuring that the superclass is fully initialized before any constructors or initializers of the subclass are executed. In contrast, default arguments are initialized starting from the child and secondary constructors. Child secondary constructor default argument Child primary constructor default argument Parent primary constructor default argument Parent.a Parent.init Parent.b Parent primary constructor Child.a Child.init 1 Child.b Child.init 2 Child primary constructor Child secondary constructor