Kotlin Functions

Using functions in Kotlin.

package academy.learnprogramming.datatypes

fun main() { // Default return type for a function is Unit, does not need to be specified. Equivalent to Void
    println(labelMultiply(3,4,"The result is:"))
    println(labelMultiply2(3,4,"The result is:"))
    println(labelMultiply3(3,4)) // Default label defined in function
    println(labelMultiply3(operand2 = 3, operand1 = 4)) // We can specify the parameters in any order if we label them

    val emp = testEmployee("Jane")
    println(emp.upperCaseFirstName()) // This is how we call functions from within a class

    // Can have functions that take an arbitrary number of arguments:
    val car1 = testCar("blue", "Toyota", 2015)
    val car2 = testCar("red", "Ford", 2016)
    val car3 = testCar("gray", "Ford", 2017)
    printColours("The colour is:", car1, car2, car3)
//    printColours2(car1, car2, car3, "The colour is:") // Won't work as varargs is not last in parameter list
    printColours2(car1, car2, car3, label = "THE COLOUR IS:") // Works

    var manyCars = arrayOf(car1, car2, car3)
//    printColours3(manyCars) // Won't work - expects testCar, gets Array<testCar>. Use spread operator instead:
    printColours3(*manyCars) // * is spread operator, means the function will accept many values:
    var manyCars2 = arrayOf(car2, car3)
    println("-----------")
    printColours3(*manyCars, *manyCars2, car2)


}

fun labelMultiply (operand1: Int, operand2: Int, label: String): String {
    return ("$label ${operand1 * operand2}")
}
// This class has a block body

// Because Kotlin this can be reduced to:
fun labelMultiply2 (operand1: Int, operand2: Int, label: String) = "$label ${operand1 * operand2}" // If single operation we can exclude 'return'. Compiler infers type
// This class has an expression body

fun labelMultiply3 (operand1: Int, operand2: Int, label: String = "This is the result:"): String { // We can use defaults in function parameters. However, cannot infer type so need :String
    return ("$label ${operand1 * operand2}")
}

fun printColours(label: String, vararg cars: testCar) { // Only one set of varargs allowed per function. Easiest if listed last
    for (car in cars) {
        println("$label ${car.colour}")
    }
}

fun printColours2(vararg cars: testCar, label: String) { // In this case the varargs aren't listed last so arguments must be labelled at instantiation (see above)
    for (car in cars) {
        println("$label ${car.colour}")
    }
}

fun printColours3(vararg cars: testCar) { // To accept array of testCars
    for (car in cars) {
        println(car.colour)
    }
}

data class testCar(val colour: String, val model: String, val year: Int)

class testEmployee (val firstName: String) {
    fun upperCaseFirstName() = firstName.toUpperCase()
}