Week3

Week 3

一. Nullability

1. Nullable types

1) Null Pointer Exception

Modern approach: to make NPE compile-time error, not run-time error

2) Null types

1
2
3
4
5
6
7
8
9
val s1:String = "always not null"
// s2 and s3 is nullable String
val s2:String? = "can be null or non-null"
val s3:String? = null

// l1 is nullable int - `Int?`
val l1 = s3?.length
// l2 is int - `Int`
val l2 = s3?.length ?: 0

3) 3 ways to call methods on a nullable type

  • if-checks
  • ?.
  • !!
1
2
3
4
5
6
7
8
s1.length

//s2.length // compile error
if (s2 != null) {
s2.length
}
s2?.length
s2!!.length

4) operators

  • Nullability operator ?.

    foo?.bar() means

    if foo != null return foo.bar()
    if foo == null return null

    1
    2
    val length:Int? = if(s != null ) s.length else null
    val length:Int? = s?.length
  • Elvis operator ?:

    foo ?: bar means

    if foo != null return foo

    if foo == null return bar

    1
    2
    val length:Int = if(s != null) s.length else 0
    val lenght:Int = s?.length ?: 0

    Both ?. and ?: come from Groovy

  • Not-null assertion operator !!

    foo!! means

    if foo != null return foo

    if foo == null return NPE

Remember Prefer ?. ?: if-checks to !!

2. Nullable types under the hood

1) Under the hood

  • Nullable types under the hood are using @Nullable @NotNull annotations

    There is no performance ahead

2) Nullable types VS Optional

$Nullable Types \neq Optional$

  • NullableType uses only one object
  • Optional type uses 2 objects (one is for the wrapper, the other is the actual type)

3) List<Int?> VS List<Int>?

1
2
3
4
5
6
7
fun foo(list1:List<Int?>,list2:List<Int>?){
list1.size
val i: Int? = list1.get(0)

list2?.size
val j: Int? = list2?.get(0)
}

3. Safe casts

  • safe cast as?

    foo as? Type means

    if foo is Type -> foo as Type

    if foo !is Type -> null

    1
    2
    val length:Int? = if(s != null ) s.length else null
    val length:Int? = s?.length

4. Importance of nullability

二. Functional Programming

1. Lambdas

1
2
3
4
5
6
7
8
9
10
11
12
13
14
val list = listOf(23, 34)
val map = mapOf(1 to 11,2 to 22)
list.any({ i: Int -> i > 0 }) // full syntax
list.any() { i: Int -> i > 0 } // when lambda is the last argument
list.any { i: Int -> i > 0 } // empty parentheses can be omitted
list.any { i -> i > 0 } // type can be omitted if it's clear from the context
list.any { it > 0 } // "it" denoted the argument if it's only one
list.any {
println("processing $it")
it > 0 // the last expression is the result
}
map.mapValues { entry -> "${entry.key} -> ${entry.value}" }
map.mapValues { (key,value) -> "$key -> $value" }
map.mapValues { (_,value) -> "$value" }

2. Common Operations on collections

  • filter : return a new list satisfying the predict

  • map : return a new list with the operation

  • any/all/none: return true if any/all/none satisfy the predict

  • find/firstOrNull return the first result, if none, return null

    first : if no element throw an exception

  • count : counts the number of the given predict

  • partition : return a pair of list list

  • groupBy

  • associateBy : duplicates removed

  • associate: create a pair T to V (V = f(T))

  • zip : length = min(list1, list2)

    zipWithNext

  • flatten : call on a List<List<>>

  • flatMap ?

3. Operation Quiz

1) Simplifying Code

  • don’t use it if it has different types in neighboring lines
  • prefer explicit parameter names if it might be confusing otherwise
  • learn the library and try to reuse the library functions as much as possible

4. Function types

1) lambda has a type (xx,xx) -> xx

1
2
val sum = { x: Int, y: Int -> x + y }
val sum2: (Int, Int) -> Int = { x, y -> x + y }

2) it can be called like a function

1
2
val isEven = { i: Int -> i % 2 == 0 }
val result: Boolean = isEven(42) // true

3) Passing a variable of function type as an argument

1
2
3
val list = listOf(1, 2, 3, 4)
list.any(isEven) // true
list.filter(isEven) // [2,4]

4) Calling lambda directly

1
2
;{ println("hey!") }() // seems strange
run { println("hey") } // use run instead

5) SAM interfaces in Java

1
2
3
4
// use lambda as parameter of SAM like Runnable ..
// In Java:
void postponeComputation(int delay, Runnable computation)
postponeComputation(1000) { println(42) }

6) Function types and nullability

1
2
3
4
//    val f1: () -> Int? = null
val f2: () -> Int? = { null }
val f3: (() -> Int)? = null
// val f4: (() -> Int)? = { null }

7) Working with a nullable function type

1
2
3
4
5
6
7
8
9
10
    val f: (() -> Int)? = null

// f() // compile error

// method 1
if (f != null){
f()
}
// method 2
f?.invoke()

5. Member references

1) Intro

1
2
3
val people = listOf<Person>(Person("Kyle", 19));
people.maxBy { it.age }
people.maxBy(Person::age) // class::member

2) Function VS lambdas

  • You can store lambda in a variable

  • but you can’t store a function in a varible

    1
    2
    val isEven = { i: Int -> i % 2 == 0 }
    val predicate = isEven
    1
    2
    3
    fun isEven(i: Int): Boolean = i % 2 == 2
    // val predicate = isEven //compile error
    val predicate = isEven()
  • use function reference instead

    1
    2
    3
    4
    fun isEven(i: Int): Boolean = i % 2 == 2
    val predicate = ::isEven
    // under the hood, it is the under line.
    // val predicate = {i: Int -> isEven(i)}

    another example

    1
    2
    3
    4
    5
    6
    fun sendEmail(person: Person, message: String) {}

    // val action = { person: Person, message: String ->
    // sendEmail(person, message)
    // }
    val action = ::sendEmail

3) Passing function reference as an argument (lambda)

1
2
3
4
fun isEven(i: Int): Boolean = i % 2 == 0
val list = listOf(1,2,3,4)
list.any(::isEven)
list.filter(::isEven)

4) bound VS unbound reference

  • regular non-bound reference

    1
    2
    3
    val agePredicate: (Person, Int) -> Boolean = Person::isOlder
    val alice = Person("Alice", 29)
    agePredicate(alice, 21)
  • bound reference

    1
    2
    3
    val alice = Person("Alice", 29)
    val agePredicate: (Int) -> Boolean = alice::isOlder
    agePredicate(21)
  • Bound to this reference

    1
    2
    3
    4
    class Person(val name: String, val age: Int) {
    fun isOlder(ageLimit: Int) = age > ageLimit
    fun getAgePredicate() = ::isOlder
    }

    Note: In the following example, ::isEven is not a bound reference

    1
    2
    3
    4
    5
    fun isEven(i: Int): Boolean = i % 2 == 0

    val list = listOf(1, 2, 3, 4)
    list.any(::isEven)
    list.filter(::isEven)

6. Returning from lambda

1) return return from function marked with fun

  • fun duplicateNonZero(list:List<Int>):List<Int>{
        return list.flatMap {
            if (it == 0) return listOf()
            listOf(it,it)
        }
    }
    println(duplicateNonZero(listOf(3,0,5))) // [],wrong
    <!--code24-->
  • Solution using label

    1
    2
    3
    4
    5
    6
    7
    fun duplicateNonZero(list:List<Int>):List<Int>{
    return list.flatMap {
    if (it == 0) return@flatMap listOf<Int>()
    listOf(it,it)
    }
    }
    println(duplicateNonZero(listOf(3,0,5))) // [3,3,5,5],solution 1
  • Solution using local function

    1
    2
    3
    4
    5
    6
    7
    8
    fun duplicateNonZeroLocalFunction(list:List<Int>):List<Int>{
    fun duplicateNonZeroElement(e:Int):List<Int>{
    if (e == 0)return listOf()
    return listOf(e,e)
    }
    return list.flatMap(::duplicateNonZeroElement)
    }
    println(duplicateNonZeroLocalFunction(listOf(3,0,5))) // [3,3,5,5]
  • Solution using anonymous function

    1
    2
    3
    4
    5
    6
    7
    fun duplicateNonZero(list: List<Int>): List<Int> {
    return list.flatMap(fun(e): List<Int> {
    if (e == 0) return listOf()
    return listOf(e, e)
    })
    }
    println(duplicateNonZero(listOf(3, 0, 5)))
  • Solution no return

    1
    2
    3
    4
    5
    6
    7
    8
    9
    fun duplicateNonZero(list: List<Int>): List<Int> {
    return list.flatMap{
    if (it == 0)
    listOf()
    else
    listOf(it,it)
    }
    }
    println(duplicateNonZero(listOf(3, 0, 5)))

3) labeled return inside forEach correspond to continue for loop

  • val list = listOf(3, 0, 5)
    list.forEach {
        if (it == 0) return@forEach
        println(it)
    }
    <!--code29-->
    
    
    
    
    
作者

Kyle-Ye

发布于

2020-02-27

更新于

2020-02-27

许可协议