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>
class Success<R>(val value: R): Response<R>()
class Failure(val error: Throwable): Response<Nothing>()</Nothing></R></R></out>

fun handle(response: Response<String>) {
val text = when (response) {
is Success -> "Success, data are: " + response.value
is Failure -> "Error"
}
print(text)
}
~~~</String>

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
Q

Higher-order function Kotlin

A

higher-order functions are functions that can take other functions as parameters or return functions as results.

26
Q

Sequence

A

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

27
Q

Invariance

A

if types have inheritance between them, the classes that use them do not inherit arcs from each other. In other words, if B inherits from A, then Class<A> will not inherit from Class<B>.
Gererics in Kotlin are invariant by default
~~~
val container: MutableList<Int> = listOf()
val container2: MutableList<Number> = container
// compile error because
// list of Int could contain Double
// this is impossible because of invariance
container2.add(4.0)
~~~</Number></Int>

28
Q

Covariance

A

Covariance refers to the preservation of type hierarchy in derived types in the same order. In other words, if B inherits from A, then Class <B> will also inherit from Class <A>. In Kotlin, the keyword “out” is used to enable covariance.

class Container<out T>(val contained: T)
// compile error because var
class Container<out T>(var contained: T)

val container: Container<Number> = Container<Int>(25)
// or 
val container: Container<Int> = Container<Int>(25)
val container2: Container<Number> = container
// but we can't change value of container
// compile error
// we can only read
container2.contained = 4.0 

out allow you to only read (output) values from that class or interface

29
Q

Contravariance

A

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<base></base> to an object of SomeClass<Derived>.</Derived>

open class Animal
class Dog : Animal()

interface Processor<in> {
fun process(item: T)
}</in>

class AnimalProcessor : Processor<Animal> {
override fun process(animal: Animal) {
animal.eat()
}
}</Animal>

fun main() {
val animalProcessor: Processor<Dog> = AnimalProcessor()
val dog = Dog()
animalProcessor.process(dog)
}</Dog>

30
Q

Type projection Kotlin

A

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<out>, to: Array<Any>) { ... }
~~~</Any></out>

31
Q

Star-projection

A

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<Box<*») {
for (box in boxes) {
println(box.value) // Any?
}
}

32
Q

Reified Kotlin

A

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 <reified T> printType() {
    println(T::class.simpleName)
}

fun main() {
    printType<Int>() // Prints: Int
    printType<String>() // Prints: String
}

if T is not reified then there would be the a compile error

33
Q

Delegation Kotlin

A

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
Q

Delegated Properties

A

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<Activity, View> {

override fun getValue(thisRef: Activity, property: KProperty<*>): View {
    val resourceId = thisRef.resources
            .getIdentifier(property.name, "id", BuildConfig.APPLICATION_ID)
    return thisRef.findViewById(resourceId)
}

}
~~~

35
Q

Initialization order

A
  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