Use-site variance

Use of covariance and contravariance within functions in Kotlin.

fun main() {

    val cars1 = mutableListOf(KotlinCar(), KotlinCar())
    val cars2: MutableList<KotlinCar> = mutableListOf()
    copyCars(cars1, cars2)

    val fords1 = mutableListOf(Ford(), Ford())
    val fords2: MutableList<Ford> = mutableListOf()
//    copyCars(fords1, fords2) // Wants a MutableList<KotlinCar>, as consistent with covariance/invariance. Once again we don't want to have to write a copyCars() function for every type of car.
    copyCars2(fords1, fords2) // Works because copyCars2 is covariant
//    copyCars2(fords1, cars2) // Doesn't initially work because in each case <T> is different (Ford and KotlinCar)
    val cars3: MutableList<KotlinCar> = mutableListOf(Ford(), Ford()) // Yet we should be able to as we can do this <-
    // If we look at our copyCars function we can see that we only read from 'source' and only write to 'destination', so we can use co- and contravariance
    copyCars3(fords1, cars1) // It works!
}

fun copyCars(source: MutableList<KotlinCar>, destination: MutableList<KotlinCar>) {
    for (car in source) {
        destination.add(car)
    }
}

fun <T> copyCars2(source: MutableList<T>, destination: MutableList<T>) {
    for (car in source) {
        destination.add(car)
    }
}

fun <T> copyCars3(source: MutableList<out T>, destination: MutableList<T>) { // Co- or contravariance declared in function - use-site variance, also known as type projection. Can also be used with function return types
// fun <T> copyCars3(source: MutableList<T>, destination: MutableList<in T>) { // This would also work
    for (car in source) {
        destination.add(car)
    }
}

open class KotlinCar {

}

class Toyota: KotlinCar() {

}

class Ford: KotlinCar() {

}

 

Kotlin declarations

Declaration rules for Kotlin.

typealias EmployeeSet = Set<Employee> // Declare at top of class

val MY_CONSTANT = 100 // Constants can be declared at the top-level in Kotlin

fun main() {
    val number: Short = 25 // Force a Float as the default would be Int
//    number = "heya" // Cannot change variable type

    // USE VAL UNLESS YOU NEED VARIABLE TO BE REASSIGNED

    val number2: Int // val can be declared without be initialised
    number2 = 25 // so now it's happy
//    number2 = 22 // val cannot be reassigned once initialised

    var number3: Int // var can be declared without be initialised
    number3 = 25
    number3 = 22 // var can be reassigned once initialised

    val employee1 = Employee("Lynn Jones", 500) // employee1 is fixed, cannot be reassigned
    employee1.name = "Lynn Smith" // However, var properties within a val can be reassigned

    val employee2: Employee
    val number4 = 100

    if (number < number4) {
        employee2 = Employee("Jane Smith", 400)
    } else {
        employee2 = Employee("Mike Watson", 150)
    } // Might look iffy but is still acceptable because employee2 will only be assigned once

    val sb: StringBuilder // <- Check declaration to see that StringBuilder is a Type Alias: @SinceKotlin("1.1") public actual typealias StringBuilder = java.lang.StringBuilder See top of class
    val employees: EmployeeSet // Example of type alias declared at start of class

    val names = listOf("John", "Jane", "Mary")
    println(names[1]) // Use square brackets to access Collection members

    for (i in 1..5) {
        println(i)
    }

    println(employee1)

    val change = 4.22
    println()
    println("$$change")
    println("To prevent the variable being printed out escape using \$change")

    val numerator = 10.99
    val denominator = 20.00
    println("The value of $numerator divided by $denominator is ${numerator/denominator}")

    println("Employee ID is ${employee1.id}") // References to other class properties are considered expressions so put them in brackets

    val filePath = "c:\\dir\\somedir" // Regular strings can sometimes require a lot of escapes
    val filePath2 = """c:\dir\somedir""" // No need for escapes with triple quoted strings!

    println()
    val nurseryRhyme = """Humpty Dumpty sat on a wall
Humpty Dumpty had a great fall
All the king's horses and all the king's men
Couldn't put Humpty together again"""
    println(nurseryRhyme)
    println()

    val nurseryRhyme2 = """Humpty Dumpty sat on a wall
        *Humpty Dumpty had a great fall
        *All the king's horses and all the king's men
        *Couldn't put Humpty together again""".trimMargin("*") // Trims everything before the quoted character. Means we can use margins in code. Default character is |
    println(nurseryRhyme2)
    println()

    val eggName = "Humpty"

    val nurseryRhyme3 = """$eggName Dumpty sat on a wall
        |$eggName Dumpty had a great fall
        |All the king's horses and all the king's men
        |Couldn't put $eggName together again""".trimMargin() // Trims everything before the quoted character. Means we can use margins in code. Default character is |
    println(nurseryRhyme3)
    println()

    println(MY_CONSTANT) // Printing top-level constant from within class
}

class Employee(var name: String, val id: Int) {
    override fun toString(): String {
        return "Employee(name='$name', id=$id)"
    }
}