Skip to content

Instantly share code, notes, and snippets.

@NaCI
Last active April 27, 2020 23:25
Show Gist options
  • Save NaCI/7aa048129132d1163462925a22005bf8 to your computer and use it in GitHub Desktop.
Save NaCI/7aa048129132d1163462925a22005bf8 to your computer and use it in GitHub Desktop.
Kotlin Notes
/**
* ANY type holds any type of object.
* The root of the Kotlin class hierarchy. Every Kotlin class has Any as a superclass.
*/
/**
* Almost everything in Kotlin is an expression with a value, but there a few exceptions.
* WHILE loops and FOR loops are not expression with a value
*/
/**
* ————————————————————————————————————————
* NULL Safety - Safe Calls
* safe call operator, written ?.
* ————————————————————————————————————————
*/
// init nullable item list with values
val listWithNulls: List<String?> = listOf("Kotlin", null, "Java", null, null, "C#")
// iterate over all items in the list
for(item in listWithNulls) {
// check if item is null or not
// let => provides to implement function for object
// print values if item is not null
item?.let { println(it+"\n") }
}
// another safe call example
bob?.department?.head?.name
// If either `person` or `person.department` is null, the function is not called:
person?.department?.head = managersPool.getManager()
/**
* ————————————————————————————————————————
* Elvis Operator
* ————————————————————————————————————————
*/
// When we have a nullable reference b, we can say "if b is not null, use it, otherwise use some non-null value":
val l: Int = if (b != null) b.length else -1
// Along with the complete if-expression, this can be expressed with the Elvis operator, written ?::
val l = b?.length ?: -1
// Note that, since throw and return are expressions in Kotlin, they can also be used on the right hand side of the elvis operator.
// This can be very handy, for example, for checking function arguments:
fun foo(node: Node): String? {
val parent = node.getParent() ?: return null
val name = node.getName() ?: throw IllegalArgumentException("name expected")
// ...
}
/**
* ————————————————————————————————————————
* !! Operator
* The third option is for NPE-lovers:
* the not-null assertion operator (!!) converts any value to a non-null type and
* throws an exception if the value is null. We can write b!!, and this will return a non-null value of b (e.g., a String in our example) or throw an NPE if b is null:
* ————————————————————————————————————————
*/
val l = b!!.length
/**
* ————————————————————————————————————————
* Safe Casts
* Regular casts may result into a ClassCastException if the object is not of the target type.
* Another option is to use safe casts that return null if the attempt was not successful:
* ————————————————————————————————————————
*/
val aInt: Int? = a as? Int
/**
* ————————————————————————————————————————
* Collections of Nullable Type
* If you have a collection of elements of a nullable type and want to filter non-null elements,
* you can do so by using filterNotNull
* ————————————————————————————————————————
*/
val nullableList: List<Int?> = listOf(1, 2, null, 4)
val intList: List<Int> = nullableList.filterNotNull()
/**
* ————————————————————————————————————————
* String Format
* ————————————————————————————————————————
*/
val numberOfItems = 13.4
"I have $numberOfItems item in my backpack"
val numberOfThings = 12
"I have ${numberOfItems + numberOfThings} item in my backpack"
/**
* ————————————————————————————————————————
* Using Range with if-else
* ————————————————————————————————————————
*/
val fish = 50
if(fish in 1..100) println("good")
val character = "b"
if(character in "a".."z") println("good")
/**
* ————————————————————————————————————————
* Switch - case alternate
* ————————————————————————————————————————
*/
val numberOfThings = 12
when(numberOfThings) {
0 -> println("No item")
20 -> println("20 item")
13,14 -> println("It is 13 or 14")
in 10..15 -> println("Between 10 and 15")
!in 0..10 -> println("Outside of the range 0 to 10")
else -> println("Perfect")
}
/**
* Triple Quoted String
*/
val herSpeech = "'Oh c'mon'"
val myText = """ And -the man says that : "Am i -the only one here!".${"\n"} Then she replied $herSpeech """
println(myText.trim())
// Output :
// And -the man says that : "Am i -the only one here!".
// Then she replied 'Oh c'mon'
/**
* String Patterns
*/
fun getMonthPattern() = """\d{2}\.\d{2}\.\d{4}"""
"13.02.1992".matches(getMonthPattern().toRegex()) // true
val month = "(JAN|FEB|MAR|APR|MAY|JUN|JUL|AUG|SEP|OCT|NOV|DEC)"
fun getPattern(): String = """\d{2} $month \d{4}"""
"13 JAN 1992".matches(getPattern().toRegex()) // true
/**
* Nothing type
* Nothing type can be used as a return type for a function that always throws an exception.
* When you call such a function, the compiler uses the information that it throws an exception.
*/
fun failWithWrongAge(age: Int?): Nothing { // If that was not return nothing nullable age param will give an error
throw IllegalArgumentException("Wrong age: $age")
}
fun checkAge(age: Int?) {
if (age == null || age !in 0..150) failWithWrongAge(age)
println("Congrats! Next year you'll be ${age + 1}.")
}
checkAge(10)
/**
* Import Rename
* as NewName
*/
import kotlin.random.Random as KRandom
import java.util.Random as JRandom
/**
* Kotlin Idioms
* https://kotlinlang.org/docs/reference/idioms.html
*/
/**
* Kotlin Standard Library
* https://kotlinlang.org/api/latest/jvm/stdlib/
*/
/**
* REFERENCES
* https://www.udacity.com/course/kotlin-bootcamp-for-programmers--ud9011
* https://kotlinlang.org/docs/reference/control-flow.html
* https://bezkoder.com/kotlin-list-mutable-list/
* https://dzone.com/articles/learning-kotlin-return-when
*/
/**
* Consts
* Consts are compile time constants. T
* Their value has to be assigned during compile time, unlike vals, where it can be done at runtime.
* This means, that consts can never be assigned to a function or any class constructor, but only to a String or primitive.
*/
const val foo = complexFunctionCall() //Not okay
val fooVal = complexFunctionCall() //Okay
const val bar = "Hello world"
// We can't declare const in class
class MyClass {
const val CONSTANT2 = "asd" // This will give error
}
// Instead we need to use compainon object in class or object class
class MyClass3 {
companion object {
const val CONSTANT2 = "123"
}
}
// or
object MyClass2 {
const val CONSTANT2 = "asd"
}
// Usage
println(MyClass2.CONSTANT2)
println(MyClass3.CONSTANT2)
/**
* EXTENSION FUNCTIONS
* It will allow to add functions to an existing class, without having access to its source code
* They will not actually modify the class they extend
* By defining an extention you do not insert new members into the class
*/
fun String.hasSpaces(): Boolean {
val found: Char? = this.find { it == ' ' }
return found != null
}
// Alternative way to represent same function
fun String.hasSpaces(): Boolean = find { it == ' ' } != null
println("My text with space".hasSpaces())
// Note: "extenstions.kt" is good file to keep all extension functions
open class AquariumPlant(var color: String, private val size: Int)
class GreenLeafyPlant(size: Int): AquariumPlant("Green", size)
fun AquariumPlant.isRed() = color.equals("Red", ignoreCase = true)
// Extension functions can't access to their extended class's private variables
fun AquariumPlant.isBig() = size < 20 // It will give error since size is private variable
fun AquariumPlant.print() = println("AquariumPlant")
fun GreenLeafyPlant.print() = println("GreenLeafyPlant")
val plant = GreenLeafyPlant(20)
println(plant.isRed()) // false
plant.print() // Output : GreenLeafyPlant
val aquariumPlant: AquariumPlant = plant
aquariumPlant.print() // Output : AquariumPlant
// Normally it should print GreenLeafyPlant but it prints AquariumPlant
// because extension functions resolved statically and at compile time (not on runtime)
// Extension properties
val AquariumPlant.isGreen: Boolean
get() = color.equals("Green", ignoreCase = true)
val plant = GreenLeafyPlant(20)
println(plant.isGreen)
// Nullable Example
fun AquariumPlant?.pull() {
this?.apply {
println("removing $this")
}
}
var nullPlant: AquariumPlant? = null
nullPlant.pull() // it will not throw exception
/**
* Extention Function as parameter
*/
// By HashMap<K, V>.() defining as that we make our build function as extention function for hashmap
fun <K, V> buildMap(build: HashMap<K, V>.() -> Unit): Map<K, V> {
val map = HashMap<K, V>()
map.build()
return map
}
// Another Example
fun <T> T.myApply(f: T.() -> Unit): T {
this.f()
return this
}
fun createString(): String {
return StringBuilder().myApply {
append("Numbers: ")
for (i in 1..10) {
append(i)
}
}.toString()
}
fun createMap(): Map<Int, String> {
return hashMapOf<Int, String>().myApply {
put(0, "0")
for (i in 1..10) {
put(i, "$i")
}
}
}
/**
* Reflections
*/
class Plant {
fun trim() {}
fun fertilize() {}
}
fun reflections() {
val classObj: KClass<Plant> = Plant::class
for (method: KFunction<*> in classObj.declaredMemberFunctions){
println("${classObj.simpleName} has ${method.name} method")
}
}
/**
* APPLY, RUN, LET
*/
// Apply return the object itself
// Apply can be very useful for calling functions on a newly created object
data class Bird(var name: String)
val bird = Bird("marti").apply { name = "sahin" }
// Difference between run and apply is
// Run returns the result of executing the lambda
// While Apply returns the object after the lambda has been applied
val bird2 = Bird("leylek")
println(bird.run { name.toUpperCase() }) // Leylek
println(bird.apply { name = name.toUpperCase() }) // Bird(name=Leylek)
// Let returns copy of changed object
// Let is particularly useful for chaining manipulations together
val result = bird.let { it.name.capitalize() } // Sahin
.let { "$it bird" } // Sahin bird
.let { it.length } // 10 - converts variable to integer
.let { it.plus(30) } // 40
println(result) // Output: 40
/**
* Operator overloading
* Kotlin allows us to provide implementations for a predefined set of operators on our types.
* These operators have fixed symbolic representation (like + or *) and fixed precedence.
*/
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int)
// Supported intervals that might be added to dates:
enum class TimeInterval { DAY, WEEK, YEAR }
fun MyDate.addTimeIntervals(timeInterval: TimeInterval, amount: Int): MyDate {
val c = Calendar.getInstance()
c.set(year + if (timeInterval == TimeInterval.YEAR) amount else 0, month, dayOfMonth)
var timeInMillis = c.timeInMillis
val millisecondsInADay = 24 * 60 * 60 * 1000L
timeInMillis += amount * when (timeInterval) {
TimeInterval.DAY -> millisecondsInADay
TimeInterval.WEEK -> 7 * millisecondsInADay
TimeInterval.YEAR -> 0L
}
val result = Calendar.getInstance()
result.timeInMillis = timeInMillis
return MyDate(result.get(Calendar.YEAR), result.get(Calendar.MONTH), result.get(Calendar.DATE))
}
operator fun MyDate.plus(timeInterval: TimeInterval) =
addTimeIntervals(timeInterval, 1)
class RepeatedTimeInterval(val timeInterval: TimeInterval, val number: Int)
operator fun TimeInterval.times(number: Int) =
RepeatedTimeInterval(this, number)
operator fun MyDate.plus(timeIntervals: RepeatedTimeInterval) =
addTimeIntervals(timeIntervals.timeInterval, timeIntervals.number)
fun task1(today: MyDate): MyDate {
return today + YEAR + WEEK
}
fun task2(today: MyDate): MyDate {
return today + YEAR * 2 + WEEK * 3 + DAY * 5
}
/**
* Invoke
* Objects with an invoke() method can be invoked as a function.
* You can add an invoke extension for any class, but it's better not to overuse it
*/
class Invokable {
var numberOfInvocations: Int = 0
private set // this variable can not be set outside of the class
operator fun invoke(): Invokable {
numberOfInvocations++
return this
}
}
fun invokeTriple(invokable: Invokable) = invokable()()()
println("Method invoked ${invokeTriple(Invokable()).numberOfInvocations} times") // Output : Method invoked 3 times
/**
* Sequences
* LAZY = fetch when needed
* EAGER = fetch immediately
* The first important thing to know is that all intermediate operations
* (the functions that return a new sequence) are not executed until a terminal operation has been called.
* Sequences are evaluated lazily to avoid examining all of the input data when it's not necessary.
* filter, map, distinct and sorted are intermediate
* toList and others are terminal
* asSequence() is used to change list to sequence
*/
// This will print nothing unless calling a terminal operation
sequenceOf("A", "B", "C")
.filter {
println("filter: $it")
true
}
// But now it will be print the values
sequenceOf("A", "B", "C")
.filter {
println("filter: $it")
true
}
.forEach {
println("forEach: $it")
}
// Output :
// filter: A
// forEach: A
// filter: B
// forEach: B
// filter: C
// forEach: C
// In the example below,
// c is never processed due to the early exit characteristic of sequences.
// The terminal function any stops further processing of elements as soon as the given predicate function yields true.
// This can greatly improve the performance of your application when working with large input collections.
val result = sequenceOf("a", "b", "c")
.map {
println("map: $it")
it.toUpperCase()
}
.any {
println("any: $it")
it.startsWith("B")
}
println(result)
// Output :
// map: A
// any: A
// map: B
// any: B
// true
/**
* Collections vs. Sequences
* Another huge benefit of sequences over collections is lazy evaluation.
* Since collection operations are not lazily evaluated you see huge performance increases when using sequences in certain scenarios.
*/
fun measure(block: () -> Unit) {
val nanoTime = measureNanoTime(block)
val millis = TimeUnit.NANOSECONDS.toMillis(nanoTime)
print("$millis ms")
}
val list = generateSequence(1) { it + 1 }
.take(50_000_000)
.toList()
measure {
list
.filter { it % 3 == 0 }
.average()
}
// 8644 ms
val sequence = generateSequence(1) { it + 1 }
.take(50_000_000)
measure {
sequence
.filter { it % 3 == 0 }
.average()
}
// 822 ms
/**
* Generic Functions
*/
// Implementation of standart library's partition() method
fun <T, V: MutableCollection<T>> Collection<T>.partitionTo(list1: V,list2: V, predicate: (T) -> Boolean): Pair<V, V> {
for(item in this) {
if(predicate(item)) {
list1.add(item)
} else {
list2.add(item)
}
}
return Pair(list1, list2)
}
val (words, lines) = listOf("a", "a b", "c", "d e").partitionTo(ArrayList(), ArrayList()) { s -> !s.contains(" ") }
val (letters, other) = setOf('a', '%', 'r', '}').partitionTo(HashSet(), HashSet()) { c -> c in 'a'..'z' || c in 'A'..'Z' }
/**
* REFERENCES
* https://www.udacity.com/course/kotlin-bootcamp-for-programmers--ud9011
* https://winterbe.com/posts/2018/07/23/kotlin-sequence-tutorial/
*/
/**
* Every method and property has PUBLIC access-modifier by default in Kotlin
*/
/**
* All Access Modifier (public, private, internal, protected)
*
* INTERNAL means variable is accessible across the same module
* this means we can use it anywhere inside of our project but if that was a library it wouldn't be exported as a function
*
* PRIVATE inside class, subclasses can't see
*
* PROTECTED inside class, subclasses CAN see
*/
// BASIC CLASS DEFINITION
class Aquarium {
var width: Int = 20
var height: Int = 40
var length: Int = 100
// fun volume(): Int = width * height * length / 1000
// Instead of function we can define property with get method
//val volume : Int
// get() = width * height * length / 1000
// Also with setter
var volume : Int
get() = width * height * length / 1000
set(value) { height = (value * 1000) / (width * length)} // setter could be defined as private (private set(value))
}
// Getting instance of aquarium
val myAquarium = Aquarium()
// Another propert sample with getter
class SimpleSpice {
val name: String = "curry"
private val spiceness: String = "mild"
val heat: Int
get() {
return when(spiceness) {
"mild" -> 5
else -> 10
}
}
}
// Constructor (like method arguments)
// With default parameters constructor overloading is not needed
class Aquarium(var width: Int = 20, var height: Int = 40, var length: Int = 100) {
var volume : Int
get() = width * height * length / 1000
set(value) { height = (value * 1000) / (width * length)}
}
val smallAquarium = Aquarium(length = 200, width = 10)
println("Aquarium is ${smallAquarium.volume} litter")
/**
* In kotlin we typically don't need secondary constructors
* Most classes only provide primary constructor
* If we define secondary constructor it must be call primary constructor by using "this" keyword
* To write good Kotlin code just use one primary constructor (with default arguments) with init blocks
*/
class Fish(val friendly: Boolean = true, volumeNeeded: Int) {
// We can call friendly property on Fish object but we can't call volumeNeeded
// That is because we didn't define it as val
val size: Int
init {
println("First init block")
}
// init is the first method to run on initialization
init {
size = if(friendly) volumeNeeded else volumeNeeded * 2
}
// Secondary constructor (Secondary constructor always called after init blocks)
constructor(): this(volumeNeeded = 5) {
println("Running secondary constructor\nFish : isFriendly{$friendly} size{$size}")
}
init {
println("Constucted fish of size $volumeNeeded final size $size")
}
}
// Calling fish with primary constructor
val fish = Fish(false, 10);
// OUTPUT :
// First init block
// Constucted fish of size 10 final size 20
// Calling fish with secondary constructor
val fish2 = Fish();
// OUTPUT :
// First init block
// Constucted fish of size 5 final size 5
// Running secondary constructor
// Fish : isFriendly{true} size{5}
/**
* INHERITANCE
* First thing to do to inherit from a class is make the class "open"
* Like classes members also are not available for subclassing by default
*/
open class Aquarium(open var width: Int = 20, var height: Int = 40, var length: Int = 100) {
open var volume : Int
get() = width * height * length / 1000
set(value) { height = (value * 1000) / (width * length)}
open val water = volume * 0.9
constructor(numberOfFish: Int): this() {
val water: Int = numberOfFish * 2000
val tank: Double = water + water * 0.1
height = (tank / (length * width)).toInt()
}
}
// Different representation for constuctor arguments for overrided values
class TowerTank(override var width: Int, height: Int): Aquarium(height = height) {
override var water = volume * 0.8
override var volume: Int
get() = (width * height * length / 1000 * PI).toInt()
set(value) {
height = (value * 1000) / (width * length)
}
}
val towerTank = TowerTank(width = 10, height = 20)
// Another Set Property Example
class PropertyExample() {
var counter = 0
var propertyWithCounter: Int? = null
set(value) {
field = value
counter++
}
}
/**
* INTERFACES AND ABSTRACT
* Abstract classes can have constructors but interfaces can not
* Both Interfaces and Abstract classes can contain implementations of methods
*/
interface AquariumAction {
fun eat()
fun jump()
fun clean()
fun swim() {
println("swim")
}
}
abstract class MyAquariumFish: AquariumAction {
abstract val color: String
override fun eat() = println("yum")
}
interface FishAction {
fun eat()
}
// Interfaces can be used as a function argument
fun feedFish(fish: FishAction) {
fish.eat()
}
class GoldFish: MyAquariumFish(), FishAction {
override val color = "orange"
override fun jump() {
println("GoldFish Jumped")
}
override fun clean() {
println("GoldFish Cleaned")
}
// We dont need to override eat function, because it has already defined in MyAquariumFish abstract class
// With FishAction interface -despite we already have eat method in MyAquariumFish class- we can use feedFish Method
}
/**
* "object" keyword uses for singleton classes
*/
/**
* INTERFACE DELEGATION
*/
interface MyFishAction {
fun eat()
}
interface MyFishColor {
val color: String
}
// SINGLETON Class
object GoldColor: MyFishColor {
override val color = "gold"
}
// SINGLETON Class
object RedColor: MyFishColor {
override val color = "red"
}
class PrintingFishAction(val food: String): MyFishAction {
override fun eat() {
println(food)
}
}
class Plecostomus(fishColor: MyFishColor = GoldColor):
MyFishAction by PrintingFishAction("a lot of algae"),
MyFishColor by fishColor {
/*
// Since we have "by" class which implements eat method we do not needed to override eat method in our class
override fun eat() {
println("eat algae")
}
*/
// override val color = "red" // We dont need to override color that's because "by fishColor" has done it already
}
// Another representation for Plecostomus class
// Since we dont have class body, we dont need curlybracers either
class Plecostomus(fishColor: MyFishColor = GoldColor):
MyFishAction by PrintingFishAction("a lot of algae"),
MyFishColor by fishColor
/**
* DATA CLASS
* Data class : main purpose is to hold data
* The primary constructor needs to have at least one parameter;
* All primary constructor parameters need to be marked as val or var;
* Data classes cannot be abstract, open, sealed or inner;
* overrides "toString" and "equals" method according to class properties
*/
data class Decorations(val rock: String, var paper: String, val cut: String)
// Equals function overrided
data class Decorations2(val rock: String, var paper: String, val cut: String) {
override fun equals(other: Any?): Boolean {
return if (other is Decorations2) {
rock == other.rock && paper == other.paper
} else {
super.equals(other)
}
}
}
val otherDecorations = Decorations2("rockiest", "paperiest", "cuttiest")
val otherDecorations2 = Decorations2("rockiest", "paperiest", "blabla")
println(otherDecorations == otherDecorations2) // Output : true
// Altered Copying
println(otherDecorations2.copy(rock = "alteredRock"))
// Kotlin Destructuring
val (rock, paper, cut) = otherDecorations2
println("Rock: $rock - Paper: $paper - Cut: $cut")
// Data classes can also uses abstract or interfaces
interface ContainerName {
val name: String
}
data class SpiceContainer(val spice: Spice): ContainerName {
val label = spice.name
override val name = "SpiceContainer"
}
// Note that the compiler only uses the properties defined inside the primary constructor for the automatically generated functions.
// To exclude a property from the generated implementations, declare it inside the class body:
data class Person(val name: String) {
var age: Int = 0
}
val person1 = Person("John")
val person2 = Person("John")
person1.age = 10
person2.age = 20
println(person1 == person2) // Output: true
// SINGLETON (object) Classes
object SingletonItem {
var name = "My Singleton Item Name"
val sayName: () -> String = {
"My name is $name"
}
}
val singletonItem1 = SingletonItem
println("singletonItem : $singletonItem1") // Output: singletonItem : SingletonItem@7a0ac6e3
println("singletonItem : ${singletonItem1.sayName()}") // Output: singletonItem : My name is My Singleton Item Name
singletonItem1.name = "Another Name"
val singletonItem2 = SingletonItem
println("singletonItem : $singletonItem2") // Output: singletonItem : SingletonItem@7a0ac6e3
println("singletonItem : ${singletonItem2.sayName()}") // Output: singletonItem : My name is Another Name
/**
* ENUMS
*/
enum class Color(val rgb: Int) {
RED(0xFF0000),
GREEN(0x00FF00),
BLUE(0x0000FF);
fun printMyColorValue() {
println("My color value is $rgb")
}
}
val colorRed = Color.GREEN
colorRed.printMyColorValue() // Output : My color value is 65280
println("Enum name : ${colorRed.name} - Enum ordinal : ${colorRed.ordinal}")
// Output : Enum name : GREEN - Enum ordinal : 1
// Making a class a sealed class helps keep all the subclasses together in one file.
/**
* Lateinit and Lazy
*/
// --------------------------------------------------------------------------------------------------
// Lateinit
// Normally, properties declared as having a non-null type must be initialized in the constructor.
// However, fairly often this is not convenient.
// For example, properties can be initialized through dependency injection, or in the setup method of a unit test.
// In this case, you cannot supply a non-null initializer in the constructor, but you still want to avoid null checks when referencing the property inside the body of a class.
// To handle this case, you can mark the property with the lateinit modifier.
// --------------------------------------------------------------------------------------------------
class Test {
lateinit var value: String
fun setup() {
value = "Ali"
}
}
// --------------------------------------------------------------------------------------------------
// Lazy
// lazy() is a function that takes a lambda and returns an instance of lazy
// which can serve as a delegate for implementing a lazy property:
// the first call to get() executes the lambda passed to lazy() and remembers the result,
// subsequent calls to get() simply return the remembered result.
// --------------------------------------------------------------------------------------------------
public class Example{
val name: String by lazy { “Ali Veli” }
}
/**
* REFERENCES
* https://www.udacity.com/course/kotlin-bootcamp-for-programmers--ud9011
* https://kotlinlang.org/docs/reference/data-classes.html
* https://blog.mindorks.com/learn-kotlin-lateinit-vs-lazy
*/
// Load image with Glide
fun ImageView.loadFromUrl(url: String, context: Context) {
Glide.with(context).load(url).into(this)
}
imageView.loadUrl(url, context)
// ViewGroup Childs
val ViewGroup.children: List
get() = (0..childCount -1).map { getChildAt(it) }
parent.children.forEach { it.visible() }
// Button disable / enable
fun Button.disableButton() {
isEnabled = false
alpha = 0.7f
}
/**
* Hides the soft input keyboard from the screen
*/
fun View.hideKeyboard(context: Context?) {
val inputMethodManager = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(this.windowToken, 0)
}
/**
* Check if the Internet connectivity is available
*/
fun Context.isInternetAvailable(): Boolean {
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetworkInfo = connectivityManager.activeNetworkInfo
return activeNetworkInfo != null && activeNetworkInfo.isConnected
}
/**
* Shows the Snackbar inside an Activity or Fragment
*
* @param messageRes Text to be shown inside the Snackbar
* @param length Duration of the Snackbar
* @param f Action of the Snackbar
*/
fun View.showSnackbar(@StringRes messageRes: Int, length: Int = Snackbar.LENGTH_LONG, f: Snackbar.() -> Unit) {
val snackBar = Snackbar.make(this, resources.getString(messageRes), length)
snackBar.f()
snackBar.show()
}
/**
* Adds action to the Snackbar
*
* @param actionRes Action text to be shown inside the Snackbar
* @param color Color of the action text
* @param listener Onclick listener for the action
*/
fun Snackbar.action(@StringRes actionRes: Int, color: Int? = null, listener: (View) -> Unit) {
setAction(actionRes, listener)
color?.let { setActionTextColor(color) }
}
// Shows the Snackbar having text and long duration
constraintLayout.showSnackbar(R.string.download_complete) {}
// Shows the Snackbar having text and short duration
constraintLayout.showSnackbar(R.string.download_complete, Snackbar.LENGTH_SHORT) {}
// Shows the Snackbar with text and action
constraintLayout.showSnackbar(R.string.download_complete) {
action(R.string.open) { Log.e("TAG", "Action item clicked") }
}
/**
* Check if the Internet connectivity is available
*/
fun Context.isInternetAvailable(): Boolean {
val connectivityManager = getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val activeNetworkInfo = connectivityManager.activeNetworkInfo
return activeNetworkInfo != null && activeNetworkInfo.isConnected
}
/**
* REFERENCES
* https://theengineerscafe.com/useful-kotlin-extension-functions-android/
*/
/**
* FUNCTIONS
* Functions are declared using the fun keyword.
* Functions that are defined inside a class or object are called member functions.
* Values are returned using the return keyword.
* Unlike other languages in kotlin we dont need to define type for no return value functions
* Even no return type is specified, return type will be Unit
* Function parameters are provided between the "()". They always have the pattern name:Type
*/
/**
* IT
* A shorthand of a single argument lambda is to use the keyword ‘it'.
* This value represents any lone that argument we pass to the lambda function
*/
fun hello(name: String, age: Int) {
println("Hello $name with age $age")
}
// Funtions returns String
fun getHelloText(name: String): String {
return "Hello $name";
}
// If a function body contains (returns) only a single expression, you reduce it to a single-line function.
fun getHelloText(name: String): String = "hello $name"
// Interestingly we dont need to specify return type for single-line function
fun getHelloText(name: String) = "hello $name" // This will also work
fun getHelloText(name: String) { return "hello $name" } // This will NOT work. it will be error for not specifying return type
/**
* Main method to play around with
*/
fun main(args: Array<String>) {
println("${getHelloText("naci").capitalize()} Hi, I am a simple string")
}
// Single Line Function Example with default arguments and Elvis operator
fun dayOfWeek(dayOfWeek: Int? = null): String = when(dayOfWeek ?: Calendar.getInstance().get(Calendar.DAY_OF_WEEK)) {
1 -> "Sunday"
2 -> "Monday"
3 -> "Tuesday"
4 -> "Wednesday"
5 -> "Thursday"
6 -> "Friday"
7 -> "Saturday"
else -> "Friday"
}
// Everything in Kotlin expression. We can use such statements like below
val isUnit = println("This is an expression")
println(isUnit)
// This is an expression
// kotlin.Unit
val temperature = 10
val isHot = temperature > 50
val message = "You are ${ if(isHot) "fried" else "safe"} fish" // Ternary operator for kotlin // we can simply use if-else block
println(message) // You are safe fish
val timeOfTheDay = args[0].toInt();
println(when {
timeOfTheDay < 12 -> "Good morning, Kotlin"
else -> "Good night, Kotlin"
})
fun getFortuneCookie(): String {
val listOfFortunes = listOf(
"You will have a great day!",
"Things will go well for you today.",
"Enjoy a wonderful day of success.",
"Be humble and all will turn out well.",
"Today is a good day for exercising restraint.",
"Take it easy and enjoy life!",
"Treasure your friends because they are your greatest fortune."
)
print("Enter your birthday: ")
val birthday = readLine()?.toIntOrNull() ?: 1
return listOfFortunes[birthday%listOfFortunes.size]
}
// LET method provides to do operations after function call // like do-while but not in loop (do-do) :)
getFortuneCookie().let { for (i in 1..3) println(it) } // this calls the method once and prints the result 3 times
// or call it in for loop several times
var fortune: String
for (i in 1..10) {
fortune = getFortuneCookie()
println("Your fortune is: $fortune")
if(fortune.contains("Take it easy")) break
}
// Default Arguments for Functions
fun shouldChangeTheWater(
day: String,
temperature: Int = 22,
isDirty: Boolean = false): Boolean {
return false;
}
// Multiple usages of the default argument methods
fun main(args: Array<String>) {
shouldChangeTheWater("Monday")
shouldChangeTheWater("Monday", isDirty = true)
shouldChangeTheWater("Monday", 23)
shouldChangeTheWater(isDirty = true, day = "Monday", temperature = 12)
}
fun canAddFish(tankSize: Double, currentFish: List<Int>, fishSize: Int = 2, hasDecorations: Boolean = true): Boolean {
return (tankSize * if (hasDecorations) 0.8 else 1.0) >= (currentFish.sum() + fishSize)
}
// Functions can be called as default arguments
fun calculateTemperatureAsCelsius(temperatureAsFahrenheit: Int) = ((temperatureAsFahrenheit - 32)*5)/9
fun shouldChangeWater(day: String, temperature: Int = calculateTemperatureAsCelsius(20)) {
//...
}
// LAMBDA Function (Anonymous or nameless function)
{println("Heelo")} ()
val swim = {println("swim\n")} // Create and assign function to a variable
swim() // Output : swim
swim // Output : res2: () -> kotlin.Unit = () -> kotlin.Unit
// Use function arguments for lambdas
var dirty = 20
val waterFilter = { dirty: Int -> dirty / 2 }
waterFilter(dirty) // Output : 10
// Define argument and return type
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }
waterFilter(dirty) // Output : 10
// If the code returns no value we use the type Unit:
var link: (String) -> Unit = {s:String -> println(s) } // just "s" is ok too (without :String)
link("test")
// Lambda function with multiple arguments
val waterFilter = {dirty: Int, divider: Int -> dirty / divider}
waterFilter(10, 4) // Output : 2
// Different presentation for same function
val waterFilter: (Int, Int) -> Int = { dirty, divider -> dirty / divider }
waterFilter(10, 4) // Output : 2
// HIGHER ORDER FUNCTION
// It is a function which takes a function as parameter
// List Filter and repeat functions are some example of higher-order functions from standard library
// Example
fun updateDirty(dirty: Int, operation: (Int) -> Int): Int {
return operation(dirty)
}
// Different calls example for high order functions
var dirty = 20
val waterFilter: (Int) -> Int = { dirty -> dirty / 2 }
fun feedFish(dirty: Int) = dirty + 10
dirty = updateDirty(dirty, waterFilter) // use lambda function as parameter of another function
dirty = updateDirty(dirty, ::feedFish) // use name function as parameter of another function
dirty = updateDirty(dirty, { dirty -> dirty + 50 }) // create function as parameter of another function
dirty = updateDirty(dirty) { dirty -> dirty + 50 } // create function as parameter - short definition
val random1 = Random.nextInt(0, 100); // Output is same for every random1 call
// random1 has a value assigned at compile time, and the value never changes when the variable is accessed.
val random2 = {Random.nextInt(0, 100)}; // // Output changes for every random2() call
// random2 has a lambda assigned at compile time,
// and the lambda is executed every time the variable is referenced, returning a different value.
val rollDice: (Int) -> Int = {
print("$it sided dice is rolling.. ")
if(it == 0) 0
else Random.nextInt(1, it) + 1
}
fun gamePlay(diceSide: Int, operation: (Int) -> Int) {
println("The dice is : ${operation(diceSide)}")
}
gamePlay(4, rollDice)
// EXTENTION FUNCTION EXAMPLE
fun List<Int>.divisibleBy(operation: (Int) -> Int): List<Int> {
var filteredList = mutableListOf<Int>()
for(item in this) {
if(operation(item) == 0) {
filteredList.add(item)
}
}
return filteredList
}
val numbers = listOf<Int>(1,2,3,4,5,6,7,8,9,0)
val divisibleNumbers = numbers.divisibleBy {
it.rem(3)
}
println(divisibleNumbers) // Output : [3, 6, 9, 0]
// Different represantation for defining extention functions
val isEven: (Int) -> Boolean = { it.rem(2) == 0 }
val isOdd: Int.() -> Boolean = { this.rem(2) == 0 }
/**
* REFERENCES
* https://www.udacity.com/course/kotlin-bootcamp-for-programmers--ud9011
*/
// AN COMPLETE EXAMPLE
val Book.weight: Double
get() = pages.times(1.5)
fun Book.tornPages(tornPages: Int): Int {
pages = if (tornPages < pages) pages.minus(tornPages) else 0
return pages
}
class Book(val title: String, val author: String, val year: String, var pages: Int) {
companion object {
const val MAX_NUMBER_OF_BOOKS_PER_USER = 30
}
fun printUrl() {
println("${Constants.BASE_URL}/$title.html")
}
fun canBorrow(borrowedBookCount: Int): Boolean {
return borrowedBookCount < MAX_NUMBER_OF_BOOKS_PER_USER
}
fun getTitleAndAuthor(): Pair<String, String> {
return title to author
}
fun getTitleAndAuthorAndYear(): Triple<String, String, String> {
return Triple(title, author, year)
}
override fun toString(): String {
return "Book(title='$title', author='$author', year='$year', pages=$pages)"
}
}
class Puppy {
fun playWithBook(book:Book) {
val tornedPageCount = Random.nextInt(0, book.pages) + 1
book.tornPages(tornedPageCount).apply {
println(
"Sorry but puppy eat $tornedPageCount pages from the ${book.title}\n" +
if (this > 0) "Only $this pages left!!" else "Nothing left.."
)
}
}
}
val myBook = Book("Satranc", "Stefan Zweig", "1997", 120)
//val(title, author) = myBook.getTitleAndAuthor()
val(title, author, year) = myBook.getTitleAndAuthorAndYear()
println("Here is your book $title written by $author in $year.")
val puppy = Puppy()
while(myBook.weight > 0) {
puppy.playWithBook(myBook)
}
/**
* GENERIC Classes
*/
class Aquarium<T> (val waterSupply: T) // Nullable
class Aquarium<T: Any> (val waterSupply: T) // NonNull
class Aquarium<T: WaterSupply> (val waterSupply: T) // Extends from specific class (Define bounds)
open class WaterSupply(var needProcessed: Boolean) {
// Writes outer class name
override fun toString(): String {
return "${this::class.simpleName}(needProcessed=$needProcessed)"
}
}
class TapWater: WaterSupply(true) {
fun addChemicalCleaners() {
needProcessed = false
}
}
class FishStoreWater: WaterSupply(false)
class LakeWater: WaterSupply(true) {
fun filter() {
needProcessed = false
}
}
class Aquarium<T: WaterSupply> (val waterSupply: T) {
fun addWater() {
check(!waterSupply.needProcessed) { "water supply needs processed" } // throws error (IllegalStateException) if condition doesn't match
println("adding water from $waterSupply")
}
}
fun genericExample() {
// val aquarium = Aquarium<TapWater>(TapWater())
val aquarium = Aquarium(TapWater()) // Kotlin automatically detects class type
aquarium.waterSupply.addChemicalCleaners()
val lakeAquarium = Aquarium(LakeWater())
lakeAquarium.waterSupply.filter()
lakeAquarium.addWater() // it will be throws exception unless we didn't call filter method
}
/**
* IN and OUT keywords for Generic Classes
* For ‘in' generic, we could assign a class of super-type to class of subtype
* For 'out' generic, we could assign a class of subtype to class of super-type
*
* In Kotlin List class uses out keyword
* The out keyword says that methods in a List can only return type E and they cannot take any E types as an argument.
* This limitation allows us to make List "covariant"
*
* In Kotlin Compare class uses in keyword
* This means that all methods in Compare can have T as an argument but cannot return T type.
* This makes Compare "contravariant"
*/
interface Production<out T> {
fun produce(): T
}
interface Consumer<in T> {
fun consume(item: T)
}
open class Food()
open class FastFood(): Food()
class Burger(): FastFood()
class FoodStore: Production<Food> {
override fun produce(): Food {
println("Produce food")
return Food()
}
}
class FastFoodStore: Production<FastFood> {
override fun produce(): FastFood {
println("Produce fastFood")
return FastFood()
}
}
class BurgerStore: Production<Burger> {
override fun produce(): Burger {
println("Produce Burger")
return Burger()
}
}
class Everybody: Consumer<Food> {
override fun consume(item: Food) {
println("Eat food")
}
}
class ModernPeople: Consumer<FastFood> {
override fun consume(item: FastFood) {
println("Eat FastFood")
}
}
class American: Consumer<Burger> {
override fun consume(item: Burger) {
println("Eat Burger")
}
}
val production1: Production<Food> = FoodStore()
val production2: Production<Food> = FastFoodStore() // Inorder to make this assignment, Production<out T> class must use out
val production3: Production<Food> = BurgerStore() // Inorder to make this assignment, Production<out T> class must use out
production1.produce() // Output : Produce food
production2.produce() // Output : Produce FastFood
production3.produce() // Output : Produce Burger
val consumer1: Consumer<Burger> = Everybody()
val consumer2: Consumer<Burger> = ModernPeople()
val consumer3: Consumer<Burger> = American()
consumer1.consume(Burger()) // Output : Eat food
consumer2.consume(Burger()) // Output : Eat FastFood
consumer3.consume(Burger()) // Output : Eat Burger
/**
* Generic Functions
*/
fun <T: WaterSupply> isWaterClean(aquarium: Aquarium<T>) {
println("aquarium water is clean: ${!aquarium.waterSupply.needProcessed}")
}
isWaterClean<TapWater>(aquarium)
isWaterClean(lakeAquarium) // we dont need to define class type explicitly, as it's already shown in the argument
/**
* Inline reified keywords
* Non reified types are only available at compile time but can't be used at runtime by your program
* If we want to use generic type as real type we need to use reified keyword (we can check if any class is my generic type class)
* All generics types are only used at compile time by Kotlin
* However at runtime, all the generic types are erased
*/
fun <R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R // Gives error
inline fun <reified R: WaterSupply> hasWaterSupplyOfType() = waterSupply is R // Correct
// We can use reified types for regular functions, even extension functions
inline fun <reified T: WaterSupply> WaterSupply.isOfType(): Boolean {
println("Type of T is ${T::class.simpleName}")
return this is T
}
aquarium.waterSupply.isOfType<LakeWater>() // false - since aquarium is type of TapWater
// Asterisk (*) means any class no matter what generic type is
inline fun <reified R: WaterSupply> Aquarium<*>.hasWaterSupplyOfTypeExtension() = waterSupply is R
aquarium.hasWaterSupplyOfTypeExtension<FishStoreWater>() // false
/**
* REFERENCES
* https://www.udacity.com/course/kotlin-bootcamp-for-programmers--ud9011
*/
/**
* Example of making custom class iterable
*/
data class MyDate(val year: Int, val month: Int, val dayOfMonth: Int) : Comparable<MyDate> {
override fun compareTo(other: MyDate): Int {
if (year != other.year) return year - other.year
if (month != other.month) return month - other.month
return dayOfMonth - other.dayOfMonth
}
}
operator fun MyDate.rangeTo(other: MyDate) = DateRange(this, other)
import java.util.Calendar
/*
* Returns the following date after the given one.
* For example, for Dec 31, 2019 the date Jan 1, 2020 is returned.
*/
fun MyDate.followingDate(): MyDate {
val c = Calendar.getInstance()
c.set(year, month, dayOfMonth)
val millisecondsInADay = 24 * 60 * 60 * 1000L
val timeInMillis = c.timeInMillis + millisecondsInADay
val result = Calendar.getInstance()
result.timeInMillis = timeInMillis
return MyDate(result.get(Calendar.YEAR), result.get(Calendar.MONTH), result.get(Calendar.DATE))
}
import java.util.*
import java.util.function.Consumer
class DateRange(val start: MyDate, val end: MyDate): Iterable<MyDate> {
override fun forEach(action: Consumer<in MyDate>?) {
super.forEach(action)
}
override fun iterator(): Iterator<MyDate> {
return object : Iterator<MyDate> {
var current: MyDate = start
override fun next(): MyDate {
if (!hasNext()) throw NoSuchElementException()
val result = current
current = current.followingDate()
return result
}
override fun hasNext(): Boolean = current <= end
}
}
override fun spliterator(): Spliterator<MyDate> {
return super.spliterator()
}
}
fun iterateOverDateRange(firstDate: MyDate, secondDate: MyDate, handler: (MyDate) -> Unit) {
for (date in firstDate..secondDate) {
handler(date)
}
}
/**
* ————————————————————————————————————————
* List - MutableList
* List is read-only (immutable), you cannot add or update items in the original list.
* MutableList inherites List and supports read/write access, you can add, update or remove items.
* List & MutableList are ordered collections in which the insertion order of the items is maintained.
* List & MutableList allows duplicates and null values
* ————————————————————————————————————————
*/
// Initialization
val myList: List<String> = listOf("ali", "veli");
val myList = mutableListOf("ali", "ahmet", "mehmet")
val myList = (0..1000).toMutableList()
val anyList = listOf("test", 2019, null)
val anyList = listOfNotNull("test", 2019, null)
val intList = List(5) { i -> i }
//intList = [0, 1, 2, 3, 4]
val intListReversed = MutableList(5) { i -> 4 - i }
//intListReversed = [4, 3, 2, 1, 0]
// Remove item
myList.removeAt(0)
//res12: kotlin.String = ahmet
// Add Item to List
// List doesn’t accept us to add items. So we can only do this with MutableList.
// Add item to list using add() method.
myList.add("newItem");
// Insert item into the list at the specified index. // starts at 0
myList.add(1, "otherItem")
// Add item to list using plus (+) operator.
myList += "kotlin"
// Add whole list to antoher list with 'addAll' method
val anotherList = listOf("item1", "item2")
myList.addAll(2, anotherList) // Add new list to specific index
// Combine multiple lists
val combinedlist1 = list1.plus(list2).plus(list3)
val combinedlist2 = list1 + list2 + list3
// Access items from List
myList.isNullOrEmpty()
myList[2]
myList.get(2)
myList.getOrElse(6, { -1 }) // -1
myList.getOrNull(6) // null
// Get sublist - These methods dont change original list, instead they create another list
myList.subList(2, 5) // [2, 3, 4]
myList.slice(2..5) // [2, 3, 4, 5]
// Take until from list
val nums = listOf(0, 1, 5, 9, 4, 2, 6, 7, 8)
nums.take(3) // [0, 1, 5]
nums.takeWhile { it < 5 } // [0, 1]
nums.takeLast(3) // [6, 7, 8]
nums.takeLastWhile { it != 4 } // [2, 6, 7, 8]
// Drop (Opposite of take)
val nums = listOf(0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
// Other methods are also same (dropWhile, dropLast, dropLastWhile)
nums.drop(3) // [3, 4, 5, 6, 7, 8]
// Update/Replace item in List
val list = mutableListOf<Any?>(0,1,2,3)
list.set(3, "three") // [0, 1, 2, three]
list[0] = "zero" // [zero, 1, 2, three]
list.replaceAll { "$it item" } // [0 item, 1 item, 2 item, 3 item]
list.replaceAll { e -> e.toString().capitalize() + ".com" }
// [0 item.com, 1 item.com, 2 item.com, 3 item.com]
list.fill(null) // [null, null, null, null]
// Remove Item With Condition
myList.removeIf { item -> item % 2 == 0 }
myList.removeIf { item in (0..100).toList() }
anyList.removeIf { item -> item is Int }
// Predicate creation
val condition: (String) -> Boolean = { text -> text.startsWith("P") }
val condition2: (String) -> Boolean = { it.startsWith("P") }
myList.removeIf(condition)
// Iterate over List
val numbers = listOf(0, 1, 2, 3, 4, 5)
// forEach() method.
numbers.forEach { i -> print(">$i ") }
// Simple for loop
for (number in numbers) {
print(">$number ")
}
// ListIterator and a while loop.
val listIterator = numbers.listIterator()
while (listIterator.hasNext()) {
val i = listIterator.next()
print(">$i ")
}
var prevItem = list.getOrElse(0, {0});
list.forEach { item ->
println("prevItem : $prevItem - item : $item\n")
if(item < prevItem) {list.remove(item)}
prevItem = item
}
// Delete items in loop
val list = mutableListOf(0,1,4,87, 33, 45, 48, 51, 23, 20, 10, 15, 13, 17, 100, 1000, 70)
var prevItem = list.getOrElse(0, {0});
val listIterator = list.listIterator()
while(listIterator.hasNext()) {
val index = listIterator.nextIndex()
val item = listIterator.next()
println("prevItem : $prevItem - item : $item\n")
if(item < prevItem) {
println("item deleted : $item\n")
listIterator.remove()
}
prevItem = item
}
// Alternate of remove items for simple cases
list.removeIf { it % 10 == 0 }
// For loop with item index
val startIndex = 1
for (i in startIndex until numbers.size) {
print(">${numbers[i]} ")
}
val list = mutableListOf(0,1,4,87, 33, 45, 48, 51, 23, 20, 10, 15, 13, 17, 100, 1000, 70)
for((index, item) in list.withIndex()) {
print("Index : $index - Item : $item\n")
}
// Reverse List
// Get a new List with revered order using reversed() method.
val numbers = mutableListOf(0, 1, 2, 3)
val reversedNumbers = numbers.reversed()
// reversedNumbers: [3, 2, 1, 0]
numbers[3] = 5
// numbers: [0, 1, 2, 5]
// reversedNumbers: [3, 2, 1, 0] - this doesnt change
// Get a reversed read-only list of the original List as a reference using asReversed() method.
// If we update item in original list, the reversed list will be changed too.
val numbers = mutableListOf(0, 1, 2, 3)
val refReversedNumbers = numbers.asReversed()
// refReversedNumbers: [3, 2, 1, 0]
numbers[1] = 8
// numbers: [0, 8, 2, 3]
// refReversedNumbers: [3, 2, 8, 0] - this changes too
// Filter List
data class Animal (val name:String)
val animals = listOf(Animal("Lion"), Animal("Elephant"), Animal("Bug"))
// animals = [Animal(name=Lion), Animal(name=Elephant), Animal(name=Bug)]
val animalsWithN = animals.filter { it.name.contains("n") }
// animalsWithN = [Animal(name=Lion), Animal(name=Elephant)]
// Check items in list
val list =
listOf("bezkoder", 2019, "kotlin", "tutorial", "bezkoder.com", 2019)
list.contains("bezkoder") // true
list.contains("zkoder") // false
"bezkoder" in list // true
list.containsAll(listOf("bezkoder", "tutorial")) // true
list.containsAll(listOf("zkoder", "tutorial")) // false
list.indexOf(2019) // 1
list.lastIndexOf(2019) // 5
list.indexOf(2020) // -1
list.lastIndexOf(2020) // -1
list.indexOfFirst { e -> e.toString().startsWith("bez") } // 0
list.indexOfFirst { e -> e.toString().startsWith("zkoder") } // -1
list.indexOfLast { e -> e.toString().endsWith("9") } // 5
list.indexOfLast { e -> e.toString().endsWith(".net") } // -1
list.find { e -> e.toString().startsWith("bez") } // bezkoder
list.find { e -> e.toString().startsWith("zkoder") } // null
list.findLast { e -> e.toString().endsWith("9") } // 2019
list.findLast { e -> e.toString().endsWith(".net") } // null
// Sort List
// Sort() method to is used for sort a Mutable List in-place, and sortDescending() for descending order.
nums.sort()
nums.sortDescending();
// Sorted() and sortedDescending() don’t change the original List. Instead, they return another sorted List.
val sortedNums = nums.sorted()
val sortedNumsDescending = nums.sortedDescending()
// Sort by custom filter
data class MyDate (val month:Int, val day:Int)
val myDates = mutableListOf(
MyDate(4, 3),
MyDate(5, 16),
MyDate(1, 29)
)
myDates.sortBy { it.month }
myDates.sortByDescending { it.month } // descending version
// Sorted by is equal to sorted -with filter.
// don’t change the original List. Instead, they return another sorted List.
val sortedDates = myDates.sortedBy { it.month }
// Sort with complex custom filters
val myDates = mutableListOf(
MyDate(8, 19),
MyDate(5, 16),
MyDate(1, 29),
MyDate(5, 10),
MyDate(8, 3)
)
myDates.sortWith(compareBy { it.month })
// Another sort example
val monthComparator = compareBy<MyDate> { it.month }
val dayThenMonthComparator = monthComparator.thenBy { it.day }
myDates.sortWith(dayThenMonthComparator)
// A complex example
class MyDateComparator {
companion object : Comparator<MyDate> {
override fun compare(o1: MyDate, o2: MyDate): Int = when {
o1.month != o2.month -> o1.month - o2.month
o1.day != o2.day -> o1.day - o2.day
else -> 0
}
}
}
myDates.sortWith(MyDateComparator)
// SOME USEFUL LIST METHODS
// joinToString
println(numbers.joinToString(prefix = "<", postfix = ">", separator = "•")) // <1•2•3•4•5•6>
val chars = charArrayOf('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q')
println(chars.joinToString(limit = 5, truncated = "...!") { it.toUpperCase().toString() }) // A, B, C, D, E, ...!
val max = nums.max()
val min = nums.min()
val sum = nums.sum()
val avg = nums.average()
val cars = listOf(Car("Mazda", 6300), Car("Toyota", 12400),
Car("Skoda", 5670), Car("Mercedes", 18600))
val car = cars.maxBy { car -> car.price }
// Kotlin List map
// The mapping operation returns a modified list by applying a transform function on each element of the list.
val nums2 = nums.map { e -> e * 2 } // every item multipled by 2 in the nums list as new list
// Kotlin List reduction
// Reduction is a terminal operation that aggregates list values into a single value.
// The reduce() method applies a function against an accumulator and each element
// (from left to right) to reduce it to a single value.
val nums = listOf(4, 5, 3, 2, 1, 7, 6, 8, 9)
val sum = nums.reduce { total, next -> total + next }
println(sum)
// reduceRight is the same logic but iterate on reversed list
// Kotlin List group by
// The groupBy() method groups elements of the original list by the key returned by the given selector function,
// applied to each element. It returns a map where each group key is associated with a list of corresponding elements.
val words = listOf("as", "pen", "cup", "doll", "my", "dog", "spectacles")
val res = nums.groupBy { if (it % 2 == 0) "even" else "odd" }
println(res)
// Result : {odd=[1, 3, 5, 7], even=[2, 4, 6, 8]}
val res2 = words.groupBy { it.length }
println(res2)
// Result : {2=[as, my], 3=[pen, cup, dog], 4=[doll], 10=[spectacles]}
// Kotlin List any
// The any() method returns true if at least one element matches the given predicate function.
val nums = listOf(4, 5, 3, 2, -1, 7, 6, 8, 9)
val r = nums.any { e -> e > 10 } // r is false
// Kotlin List all
// The all() returns true if all elements satisfy the given predicate function.
val nums = listOf(4, 5, 3, 2, -1, 7, 6, 8, 9)
val r = nums.all { e -> e > 0 } // r is false
// Some examples
val spices = listOf("curry", "pepper", "cayenne", "ginger", "red curry", "green curry", "red pepper" )
val newSpices = spices.withIndex().filter { (index,item) -> index < 3 && item.contains('c') }.map { it.value }
val newSpices2 = spices.take(3).filter { item -> item.contains('c') }
println(newSpices)
/**
* Pairs (key-value)
*/
val equipment = "fishnet" to "catching" // Simple Pair definition
val equipment = "fishnet" to 12 to "bruce" to "lee" to true // Pair chain
equipment.first.first.second // Output: bruce
equipment.first // Output: (((fishnet, 12), bruce), lee)
val equipment = ("fishnet" to 12) to ("bruce" to "lee") // We can use paranthesis for pairs
equipment.second.first // Output: bruce
// We can use pairs to return multiple variables on functions.
// And we can use them as destructure the pair into variables
fun giveMeATool(): Pair<String, String> {
return ("fishnet" to "cactching fish")
}
val(tool, use) = giveMeATool()
println("Tool $tool usage is $use") // Output: Tool fishnet usage is cactching fish
// Pair could be convert to list with toList() method
equipment.toList() // Output : [fishnet, catching]
/**
* Triple
* is just like pair but with three variables
*/
val (a, b, c) = Triple(2, "x", listOf(null))
/**
* MAPS
* Map is list of pairs
*/
val cures: MutableMap<String, String> = mutableMapOf("white spots" to "Ich", "red sores" to "hole diseases")
cures.putIfAbsent("white spots", "whites"); // It will not be added since the key already exist in map
cures.getOrDefault("purple spots", "Nope") // Output: Nope
println(cures.getOrPut("purple spots") {
println("new value added for purple spots")
val myVal = "Purples"
myVal
})
// Output :
// new value added for purple spots
// Purples
/**
* SET
* Set<T> stores unique elements; their order is generally undefined.
* null elements are unique as well: a Set can contain only one null.
* Two sets are equal if they have the same size, and for each element of a set there is an equal element in the other set.
* The default implementation of Set – LinkedHashSet – preserves the order of elements insertion
*/
val numbers = setOf(1, 2, 3, 4)
println("Number of elements: ${numbers.size}") // 4
if (numbers.contains(1)) println("1 is in the set") // true
val numbersBackwards = setOf(4, 3, 2, 1)
println("The sets are equal: ${numbers == numbersBackwards}") // true (order is not important)
// Example of SET and MAP
val allBooks = setOf("Macbeth", "Romeo and Juliet", "Hamlet", "A Midsummer Night's Dream")
val library = mapOf("Shakespeare" to allBooks)
println(library) // Output : {Shakespeare=[Macbeth, Romeo and Juliet, Hamlet, A Midsummer Night's Dream]}
println(library["Shakespeare"]?.elementAt(2)) // Output : Hamlet
// Return true if all customers are from a given city
fun Shop.checkAllCustomersAreFrom(city: City): Boolean =
customers.all { it.city == city }
// Return true if there is at least one customer from a given city
fun Shop.hasCustomerFrom(city: City): Boolean =
customers.any { it.city == city }
// Return the number of customers from a given city
fun Shop.countCustomersFrom(city: City): Int =
customers.count { it.city == city }
// Return a customer who lives in a given city, or null if there is none
fun Shop.findCustomerFrom(city: City): Customer? =
customers.find { it.city == city }
/**
* "FlatMap" example
*/
data class Shop(val name: String, val customers: List<Customer>)
data class Customer(val name: String, val city: City, val orders: List<Order>) {
override fun toString() = "$name from ${city.name}"
}
data class Order(val products: List<Product>, val isDelivered: Boolean)
data class Product(val name: String, val price: Double) {
override fun toString() = "'$name' for $price"
}
data class City(val name: String) {
override fun toString() = name
}
// Return the most expensive product that has been ordered by the given customer
fun getMostExpensiveProductBy(customer: Customer): Product? =
customer.orders
.flatMap{
it.products
}.maxBy{
it.price
}
// Same method, different representation
fun getMostExpensiveProductBy(customer: Customer): Product? =
customer.orders
.flatMap(Order::products)
.maxBy(Product::price)
// Another example
// Return all products the given customer has ordered
fun Customer.getOrderedProducts(): List<Product> =
orders.flatMap(Order::products)
// Return all products that were ordered by at least one customer
fun Shop.getOrderedProducts(): Set<Product> =
// customers.flatMap(Customer::orders).flatMap(Order::products).toSet()
// Another representation
customers.flatMap(Customer::getOrderedProducts).toSet()
/**
* associateBy
* Returns a Map containing the elements from the given sequence indexed by the key
* returned from keySelector function applied to each element.
*/
data class Person(val firstName: String, val lastName: String) {
override fun toString(): String = "$firstName $lastName"
}
val scientists = listOf(Person("Grace", "Hopper"), Person("Jacob", "Bernoulli"), Person("Johann", "Bernoulli"))
val byLastName = scientists.associateBy { it.lastName }
// Jacob Bernoulli does not occur in the map because only the last pair with the same key gets added
println(byLastName) // {Hopper=Grace Hopper, Bernoulli=Johann Bernoulli}
val byLastName2 = scientists.associateBy({ it.lastName }, { it.firstName })
// Jacob Bernoulli does not occur in the map because only the last pair with the same key gets added
println(byLastName2) // {Hopper=Grace, Bernoulli=Johann}
/**
* Partition
*/
val numbers = listOf("one", "two", "three", "four")
val (match, rest) = numbers.partition { it.length > 3 }
// Return customers who have more undelivered orders than delivered
fun Shop.getCustomersWithMoreUndeliveredOrders(): Set<Customer> = customers.filter {
val (delivered, undelivered) = it.orders.partition { order -> order.isDelivered }
undelivered.size > delivered.size
}.toSet()
/**
* FOLD
* This function helps to accumulate value starting with initial value,
* then apply operation from left to right to current accumulator value and each element.
* foldRight() -> right to letf
*/
println(listOf(1, 2, 3, 4, 5).fold(1) { mul, item -> mul * item }) // (Initial)1 * 1*2*3*4*5 => 120
println(listOf(1, 2, 3, 4, 5).fold(3) { mul, item -> mul * item }) // 3 * 1*2*3*4*5 => 360
println(listOf(71, 79, 67, 68, 64).fold("Tadaaa:") { char, item -> char + item.toChar() }) // Output: Tadaaa:GOCD@
// Another example for fold
fun Customer.getOrderedProducts(): List<Product> =
orders.flatMap(Order::products)
/**
* Intersect
* Intersect gets same values on two lists
*/
// Return the set of products that were ordered by all customers
fun Shop.getProductsOrderedByAll(): Set<Product> {
val allOrderedProducts = customers.flatMap { it.getOrderedProducts() }.toSet()
// Gets allOrderedProducst as initial and crop its value by the customer order values
return customers.fold(allOrderedProducts, { orderedByAll, customer ->
orderedByAll.intersect(customer.getOrderedProducts())
})
}
fun main(args: Array<String>) {
val item1 = Product("Köfte", 12.3)
val item2 = Product("Pattes", 22.3)
val item3 = Product("Dolma", 32.3)
val item4 = Product("Antrikot", 2.3)
val item5 = Product("Bezelye", 12.7)
val customer1 = Customer("Ali",City("Istanbul"), listOf(Order(listOf(item1, item3), true), Order(listOf(item1, item5), false)))
val customer2 = Customer("Ayse",City("Ankara"), listOf(Order(listOf(item1, item2, item3), true)))
val customer3 = Customer("Veli",City("Bolu"), listOf(Order(listOf(item5), true), Order(listOf(item1, item5), false)))
val shop = Shop("lokanta", listOf(customer1, customer2, customer3))
println(shop.getProductsOrderedByAll()) // Output: ['Köfte' for 12.3]
}
// Count the amount of times a product was ordered.
// Note that a customer may order the same product several times.
fun Shop.getNumberOfTimesProductWasOrdered(product: Product): Int {
return customers.flatMap(Customer::getOrderedProducts).count { it == product }
// Long way
// return customers.flatMap(Customer::getOrderedProducts).groupBy { it.name }.getValue(product.name).size
}
/**
* distinct()
* Returns a sequence containing only distinct elements from the given sequence.
*/
val list = listOf('a', 'A', 'b', 'B', 'A', 'a')
// just like distinct we can use toSet() method either
println(list.distinct()) // [a, A, b, B]
println(list.distinctBy { it.toUpperCase() }) // [a, b]
/**
* REFERENCES
* https://www.udacity.com/course/kotlin-bootcamp-for-programmers--ud9011
* https://kotlinlang.org/docs/reference/collections-overview.html
* https://kotlinlang.org/api/latest/jvm/stdlib/kotlin/-triple/
* https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.sequences/associate-by.html
* https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.collections/join-to-string.html
* https://bezkoder.com/kotlin-list-mutable-list/
* https://bezkoder.com/kotlin-fold/
* https://dzone.com/articles/learning-kotlin-return-when
* http://zetcode.com/kotlin/lists/
*/
/**
* LOOPS
*/
// Simple For Loops
for(i in 'b'..'h') print(i) //bcdefgh
for(i in 1..5) print(i) //12345
for(i in 5 downTo 1) print(i) //54321
for(i in 1..16 step 3) print(i) //147101316
// Continue and Break
outer@ for (n in 2..100) {
for (d in 2 until n) {
if (n % d == 0) continue@outer
}
println("$n is prime\n")
}
// Usage of WHEN statement
// We can avoid the next evolution by creating a variable and returning it directly:
fun step2(number: Int):String {
when (number) {
0 -> return "Zero"
1 -> return "One"
2 -> return "Two"
}
return ""
}
// And, now, we get to the cool part — we can just return the when !
fun step3(number: Int):String {
return when (number) {
0 -> "Zero"
1 -> "One"
2 -> "Two"
else -> ""
}
}
// REPEAT (continue and break keyword are unusable inside repeat function)
repeat(3) { // number of repeat count
val randomDay = Random(4).nextInt()
println(randomDay.toString())
}
// DO WHILE
do {
val fortune = getFortuneCookie(getBirthday())
println(fortune)
} while (!fortune.contains("Take it easy"))
/**
* REFERENCES
* https://www.udacity.com/course/kotlin-bootcamp-for-programmers--ud9011
* https://kotlinlang.org/docs/reference/control-flow.html
* https://dzone.com/articles/learning-kotlin-return-when
*/
import kotlin.math.absoluteValue
enum class Directions(val shortName: String? = null) {
NORTH("n"),
SOUTH("s"),
EAST("e"),
WEST("w"),
START,
END;
}
class Location (val width: Int = 4, val height: Int = 4) {
private val map = Array(width) { arrayOfNulls<String>(height) }
var currentLocation = Pair (0,0)
fun updateLocation(direction: Directions) {
when (direction) {
Directions.NORTH -> {
currentLocation = Pair(currentLocation.first, (currentLocation.second + 1).rem(height))
}
Directions.SOUTH -> {
currentLocation = Pair(currentLocation.first, (currentLocation.second - 1).absoluteValue.rem(height))
}
Directions.EAST -> {
currentLocation = Pair((currentLocation.first + 1).rem(width), currentLocation.second)
}
Directions.WEST -> {
currentLocation = Pair((currentLocation.first - 1).absoluteValue.rem(width), currentLocation.second)
}
}
}
fun getDescription ():String? {
return map[currentLocation.first.rem(width)][currentLocation.second.rem(height)]
}
init {
map[0][0] = "You are at the start of your journey. [n / e]"
map[0][1] = "The road stretches before you. It promises beauty and adventure. [ n / s / e]"
map[0][2] = "The road still stretches before you. It rains and the water forms puddles. [ n / s / e]"
map[0][3] = "It is getting much colder and you wish you had a wool coat. [ s / e]"
map[1][0] = "The narrow path stretches before you. You are glad you are on foot. [ n / e /w]"
map[1][1] = "It is getting warmer. You smell moss, and marmot scat. You are stung by a mosquito. [ n / s / e / w]"
map[1][2] = "You decide to sit on your backside and slide down a vast snowfield. [ n / s / e /w]"
map[1][3] = "You have climbed to the top of a snowy mountain and are enjoying the view. [ w / s / e]"
map[2][0] = "You cross an old stone bridge. Your hear the murmuring of water. And another grumbling sound. [ n / e / w]"
map[2][1] = "A bridge troll appears. It swings a club and demands gold. You give them all your coins. [ n / s / e /w]"
map[2][2] = "It is getting cooler. A dense fog rolls in. You can hear strange voices. [ n / s / e / w]"
map[2][3] = "The foothills promise a strenuous journey. You thread your way around gigantic boulders. [ s / e / w ]"
map[3][0] = "Your journey continues. A fox runs across the path with a chicken in its mouth.[ n / e ]"
map[3][1] = "In the distance, you see a house. You almost bump into a farmer with a large shotgun. They pay you no heed. [ n / s / w ]"
map[3][2] = "It is getting hot, and dry, and very, very quiet. You think of water and wish you had brought a canteen.[ n / s / w ]"
map[3][3] = "You have reached the infinite desert. There is nothing here but sand dunes. You are bitten by a sand flea.[ s / w ] "
}
}
class Game {
private var path: MutableList<Directions> = mutableListOf(Directions.START)
val map = Location()
private val north = {
path.add(Directions.NORTH)
true
}
private val south = {
path.add(Directions.SOUTH)
true
}
private val east = {
path.add(Directions.EAST)
true
}
private val west = {
path.add(Directions.WEST)
true
}
private val end: () -> Boolean = {
path.add(Directions.END)
println("Game Over\n ${path.joinToString(separator = " - ")}")
path.clear()
false
}
private fun move(where: () -> Boolean): Boolean {
return where()
}
fun makeMove(argument: String?): Boolean {
/*
if(Directions.values().toList().map { it.shortName }.contains(argument)) {}
*/
return when(argument) {
Directions.WEST.shortName -> {
map.updateLocation(Directions.WEST)
move(west)
}
Directions.EAST.shortName -> {
map.updateLocation(Directions.EAST)
move(east)
}
Directions.NORTH.shortName -> {
map.updateLocation(Directions.NORTH)
move(north)
}
Directions.SOUTH.shortName -> {
map.updateLocation(Directions.SOUTH)
move(south)
}
else -> move(end)
}
}
}
fun main(args: Array<String>) {
val game = Game()
while (true) {
print("Enter a direction: n/s/e/w:")
if(!game.makeMove(readLine())) {
break
} else {
println(game.map.getDescription())
}
}
}
/**
* REFERENCES
* https://www.udacity.com/course/kotlin-bootcamp-for-programmers--ud9011
*/
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment