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 | val s1:String = "always not null" |
3) 3 ways to call methods on a nullable type
- if-checks
- ?.
- !!
1 | s1.length |
4) operators
Nullability operator
?.
foo?.bar()
meansif foo != null return foo.bar()
if foo == null return null1
2val length:Int? = if(s != null ) s.length else null
val length:Int? = s?.lengthElvis operator
?:
foo ?: bar
meansif foo != null return foo
if foo == null return bar
1
2val length:Int = if(s != null) s.length else 0
val lenght:Int = s?.length ?: 0Both ?. and ?: come from Groovy
Not-null assertion operator
!!
foo!!
meansif 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
annotationsThere 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 | fun foo(list1:List<Int?>,list2:List<Int>?){ |
3. Safe casts
safe cast
as?
foo as? Type
meansif foo is Type -> foo as Type
if foo !is Type -> null
1
2val length:Int? = if(s != null ) s.length else null
val length:Int? = s?.length
4. Importance of nullability
二. Functional Programming
1. Lambdas
1 | val list = listOf(23, 34) |
2. Common Operations on collections
filter
: return a new list satisfying the predictmap
: return a new list with the operationany/all/none
: return true if any/all/none satisfy the predictfind/firstOrNull
return the first result, if none, return nullfirst
: if no element throw an exceptioncount
: counts the number of the given predictpartition
: return a pair of list listgroupBy
associateBy
: duplicates removedassociate
: 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 | val sum = { x: Int, y: Int -> x + y } |
2) it can be called like a function
1 | val isEven = { i: Int -> i % 2 == 0 } |
3) Passing a variable of function type as an argument
1 | val list = listOf(1, 2, 3, 4) |
4) Calling lambda directly
1 | ;{ println("hey!") }() // seems strange |
5) SAM interfaces in Java
1 | // use lambda as parameter of SAM like Runnable .. |
6) Function types and nullability
1 | // val f1: () -> Int? = null |
7) Working with a nullable function type
1 | val f: (() -> Int)? = null |
5. Member references
1) Intro
1 | val people = listOf<Person>(Person("Kyle", 19)); |
2) Function
VS lambdas
You can store lambda in a variable
but you can’t store a function in a varible
1
2val isEven = { i: Int -> i % 2 == 0 }
val predicate = isEven1
2
3fun isEven(i: Int): Boolean = i % 2 == 2
// val predicate = isEven //compile error
val predicate = isEven()use function reference instead
1
2
3
4fun 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
6fun 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 | fun isEven(i: Int): Boolean = i % 2 == 0 |
4) bound VS unbound reference
regular non-bound reference
1
2
3val agePredicate: (Person, Int) -> Boolean = Person::isOlder
val alice = Person("Alice", 29)
agePredicate(alice, 21)bound reference
1
2
3val alice = Person("Alice", 29)
val agePredicate: (Int) -> Boolean = alice::isOlder
agePredicate(21)Bound to this reference
1
2
3
4class 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 reference1
2
3
4
5fun 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
7fun 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 1Solution using local function
1
2
3
4
5
6
7
8fun 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
7fun 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
9fun 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-->