Koans
Scope: apply
- The context object is available as a receiver (this)
- The return value is the object itself
- Use apply for code blocks that don’t return a value and mainly operate on the members of the receiver object.
The common case for apply is the object configuration. Such calls can be read as “ apply the following assignments to the object.”
val adam = Person("Adam").apply {
age = 32
city = "London"
}
Exercise: Create your own apply method
Top level function
- Top-level functions are functions inside a Kotlin package that are defined outside of any class, object, or interface.
- This means that they are functions you call directly, without the need to create any object or call any class
Top-level functions in Kotlin can be used as a replacement for the static utility methods inside helper classes we code in Java. Let’s look at how to define a top-level function in Kotlin.
package demo.utils
fun checkUserStatus(): String {
return "online"
}
In the code above, we defined a packagedemo.utils
inside a file called UserUtils.kt
and also defined a top-level utility function called checkUserStatus()
inside this same package and file.
Java Interoperability
package demo.utils
public class UserUtilsKt {
public static String checkUserStatus() {
return "online";
}
}
This means that Java callers can simply call the method by referencing its generated class, just like for any other static method.
Static Methods
- As mentioned above, Kotlin represents package-level functions as static methods.
- Kotlin can also generate static methods for functions defined in named objects or companion objects
- If you annotate those functions as
@JvmStatic
, the compiler will generate both :- a static method in the enclosing class of the object
- and an instance method in the object itself
- If you annotate those functions as
For example:
class C {
companion object {
@JvmStatic fun callStatic() {}
fun callNonStatic() {}
}
}
Now, callStatic() is static in Java while callNonStatic() is not:
C.callStatic(); // works fine
C.callNonStatic(); // error: not a static method
C.Companion.callStatic(); // instance method remains
C.Companion.callNonStatic(); // the only way it works
Late init
Equals Operators
Structural Equality (==
=> Object.equals())
==
operator is used to compare the data of two variables==
operator in Kotlin only compares the data or variables, whereas in Java or other languages==
is generally used to compare the references- The negated counterpart of == in Kotlin is != which is used to compare if both the values are not equal to each other.
Referential equality (===
)
===
operator is used to compare the reference of two variable or object- It will only be true if both the objects or variables pointing to the same object.
- The negated counterpart of === in Kotlin is !== which is used to compare if both the values are not equal to each other.
For values which are represented as primitive types at runtime (for example, Int), the === equality check is equivalent to the == check
Inner Class
- A nested class marked as inner can access the members of its outer class
- Inner classes carry a reference to an object of an outer class
class Outer {
private val bar: Int = 1
inner class Inner {
fun foo() = bar
}
}
Type aliases
- Type aliases provide alternative names for existing types
- If the type name is too long you can introduce a different shorter name and use the new one instead
It’s useful :
- To shorten long generic types
- For instance, it’s often tempting to shrink collection types:
typealias NodeSet = Set<Network.Node>
typealias FileTable<K> = MutableMap<K, MutableList<File>>
- Different aliases for function types:
typealias MyHandler = (Int, String, Any) -> Unit
typealias Predicate<T> = (T) -> Boolean
- Have new names for inner and nested classes:
class A {
inner class Inner
}
class B {
inner class Inner
}
typealias AInner = A.Inner
typealias BInner = B.Inner
Nothing type
- This class has no instance, and it is used to represent a value which never exists
- This class is also used to represent a return type from a method that will never return
- If we use null to initialize a value of an inferred type and there is no other information that can be used to determine a more specific type, the compiler considers it as Nothing? type.
val variable = null
//compiler will read this as
// val variable : Nothing? = null
val list = listOf(null)
//compiler will read this as
//val list : List<Nothing?> = listOf(null)
- Nothing has no type and can also be used to mark code locations that can never be reached.
- Let’s say we have a method which throws an exception. The return type of that method would be Nothing type.
fun throwException(message: String): Nothing {
throw IllegalArgumentException(message)
}
throw is an expression in Kotlin and can be used as a part of Elvis operator also. The above code can be replaced with :
val personName = person.name ?: throwException("Name required")