Week4

Week4

一. Properties

1. Properties

1) Read-only & mutable properties

Kotlin Java
property Field + accessor(s)
val Filed + getter
var Filed + getter+setter

Sample :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Java
public final class Person {
@NotNull
private final String name;
private int age;

public Person(@NotNull String name, int age) {
this.name = name;
this.age = age;
}

@NotNull
public String getName() {
return name;
}

public int getAge() {
return age;
}

public void setAge(int age) {
this.age = age;
}
}
1
2
// Kotlin
class Person(val name: String, var age: Int)

2) Backing field might be absent

Kotlin Java
property accessor(s)
val getter
var getter+setter

Sample :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// Java
public final class Rectangle {
private int height;
private int width;

public int getHeight() {
return height;
}

public int getWidth() {
return width;
}

public boolean isSquare() {
return height == width;
}
}
1
2
3
4
5
6
7
8
9
10
// Kotlin
class Rectangle(val height: Int, val width: Int) {
val isSquare: Boolean
get() = height == width
}

fun main() {
val rectangle = Rectangle(2, 3)
println(rectangle.isSquare) // false
}

3) Stored value or not?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// the value is stored
val foo1 = run {
println("1. Calculating the answer...")
42
}

// the value is calculated on each process
val foo2: Int
get() {
println("2. Calculating the answer...")
return 42
}

fun main(){
println("foo1:")
println("$foo1 $foo1")
println("foo2:")
println("$foo2 $foo2")
}

4) Field

  • You can access field only inside accessors

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    class StateLogger {
    var state = false
    set(value) {
    println(
    "state has changed: " +
    "$field -> $value"
    )
    field = value
    }
    }
  • No backing field will be generated if you define accessors (custom getter and setter) and don’t use a field keyword

    1
    2
    3
    4
    5
    6
    7
    8
    9
    enum class State { ON, OFF }
    class StateLogger {
    private var boolState = false
    var state: State
    get() = if (boolState) ON else OFF
    set(value: State) {
    boolState = value == ON
    }
    }

5) Default accessors

1
2
3
4
5
6
7
class A {
var trivialProperty: String = "abc"
// get() = field
// set(value) {
// field = value
// }
}

You can always use property instead of getter or setter (Under the hood, getter and setter will be called)

And inside the class, optimization performed by the compiler are possible (using the filed directly, but only with the trial/default case)

6) Change visibility of a setter

  • You want a var mutable property to be accessible only as a read-only property outside of the class

    1
    2
    3
    4
    5
    6
    7
    8
    class LengthCounter {
    var counter: Int = 0
    private set

    fun addWord(word: String) {
    counter += word.length
    }
    }

2. More about properties

1) override property

2) Smart cast

3. Lazy and late initialization

1) by lazy

1
2
3
4
5
6
7
8
9
10
11
12
13
val value:String = run {
println("1. computed")
"1. Hello"
}

val lazyValue:String by lazy {
println("2. computed")
"2. Hello"
}

fun main(){
// 1. computed
}

2) lateinit

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// lateinit is still unsafe and a runtime exception may be thrown, but with a detailed message
lateinit var myData: String
// lateinit's constraints
// 1. it can't be val
// 2. it can't have a primitive type

// check whether `lateinit var` was initialized
class MyClass{
lateinit var lateinitVar:String
fun initializationLogic(){
println(this::lateinitVar.isInitialized) // false
lateinitVar = ""
println(this::lateinitVar.isInitialized) // true
}
}
fun main(){
var c = MyClass()
c.initializationLogic()
}

二. Object-oriented Programming

1. OOP in Kotlin

1) modifier intro.

Modifier Description
final (default) cann’t be overridden
open can be overridden
abstract must be overridden
override (mandatory) Overrides a member in a superclass or interface
Visibility Modifier Class member Top-level declaration
public (default) visible everywhere visible everywhere
internal visible in the mudule visible in the module
protected visible in the class and the subclasses (in Java, it includes the same package)
private visible in the class visible in the file
  • Visibility modifiers and Java

    Kotlin modifier JVM level
    public public
    internal public & name mangling
    protected protected
    private private / package private

2) Package structure

1
2
3
4
5
6
7
8
9
10
11
12
13
// Java
package:org.kyle.store
City.java
Customer.java
Order.java

// Kotlin
package:store
StoreModel.kt
(File Structure)
Class City
Class Customer
Class Order
  • one kotlin file may contain several classes and top-level functions

2. Constructors, Inheritance syntax

1) Constructor

  • Primer Constructor

  • var/val on a parameter creates a property

    1
    2
    3
    class Person(val name: String) {

    }

    the same as

    1
    2
    3
    4
    5
    6
    class Person(name: String){
    val name:String
    init {
    this.name = name
    }
    }
  • Changing visibility of a constructor

    1
    2
    class InternalComponent
    internal constructor(name:St)
  • Each Secondary constructor must using this to call another constructor

2) Syntax for inheritance

  • Same syntax for extends and implements

    1
    2
    3
    4
    5
    interface Base
    class BaseImpl : Base

    open class Parent
    class Child: Parent()
    1
    2
    open class Parent(val name: String)
    class Child(name: String) : Parent(name)
    1
    2
    3
    4
    open class Parent(val name: String)
    class Child : Parent {
    constructor(name: String, param: Int) : super(name)
    }
  • init order

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    open class Parent(){
    init {
    println("Parent")
    }
    }

    class Child:Parent(){
    init {
    println("Child")
    }
    }
    val child = Child()
    // Parent
    // Child
  • Overriding a property

    Actually you are overriding a getter, not a field

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    open class Parent() {
    open val foo = 1

    init {
    println(foo)
    }
    }

    class Child : Parent() {
    override val foo = 2
    }

    val child = Child()
    // 0

3. Class modifiers

1) enum

  • enum class Color(val r: Int, val g: Int, val b: Int) {
        BLUE(0, 0, 255), ORANGE(255, 165, 0), RED(255, 0, 0);
        fun rgb() = (r * 256 + g) * 256 + b
    }
    
    fun main(){
        println(BLUE.r) // 0
        println(BLUE.rgb()) // 255
    }
    <!--code20-->
    
1
2
3
4
5
6
7
8
sealed class Expr
class Num(val value: Int) : Expr()
class Sum(val left: Expr, val right: Expr) : Expr()

fun eval(e: Expr): Int = when (e) {
is Num -> e.value
is Sum -> eval(e.left) + eval(e.right)
}

4) nested and inner class

  • Inner class stores the reference to an outer class (Memory leak)
In Java In Kotlin Class declared within another class
static class A class A (by default) nested class
class A (by default) inner class A inner class
1
2
3
4
5
6
7
8
9
10
class A {
class B {
inner class C() {
fun main() {
println(this)
println(this@B)
}
}
}
}
1
2
3
4
5
6
7
8
9
10
11
class A {
inner class B {
inner class C() {
fun main() {
println(this)
println(this@B)
println(this@A)
}
}
}
}

5) Class delegation

To implement the interface by delegation to this instance using keyword by

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
interface Repository {
fun getById(id: Int): String
fun getAll(): List<String>
}

interface Logger {
fun logAll()
}

class Controller(
repository: Repository,
logger: Logger
) : Repository by repository, Logger by logger {
fun use(controller:Controller){
controller.logAll()
}
}

4. ?? Objects, object expressions & companion objects

5. Constants

  • const (for primitive types and String)

    compile-time constants, the value is inlined

    1
    const val answer = 42
  • @JvmField (eliminates accessors)

    exposes a Kotlin property as a field in Java

  • @JvmStatic @JvmField const

    1
    2
    3
    4
    5
    6
    7
    8
    9
    object SuperComputer{
    @JvmStatic
    val answer = 42
    }

    fun main(){
    println(SuperComputer.answer)
    // System.out.println(SuperComputer.getAnswer())
    }
    1
    2
    3
    4
    5
    6
    7
    8
    9
    object SuperComputer{
    @JvmField
    val answer = 42
    }

    fun main(){
    println(SuperComputer.answer)
    // System.out.println(SuperComputer.answer)
    }
    1
    2
    3
    4
    5
    6
    7
    8
    object SuperComputer{
    const val answer = 42
    }

    fun main(){
    println(SuperComputer.answer)
    // System.out.println(42)
    }

6. Generics

  • T can be nullable or not nullable

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    fun <T> List<T>.filter(predicate:(T) -> Boolean):List<T> {
    return this
    }

    fun use1(ints:List<Int>){
    ints.filter { it > 0 }
    }
    fun use2(strings:List<String?>){
    strings.filter { !it.isNullOrEmpty() }
    }
  • Nullable generic argument

    1
    2
    3
    4
    5
    6
    7
    8
    fun <T> List<T>.firstOrNull(): T? {
    ...
    }

    val ints = listOf(1, 2, 3)
    val i = ints.firstOrNull() // 1
    val j = listOf<Int>().firstOrNull() // null
    val k = listOf(null, 1).firstOrNull() // null
  • Non-nullable upper bound (Any)

    1
    2
    3
    4
    5
    6
    7
    fun <T : Any> foo(list: List<T>) {
    for (element in list) {

    }
    }

    //val a = foo(listOf(1,null)) // compile error

    Nullable upper bound (Any?)

  • Comparable upper bound

  • Multiple constraints for a type parameter (using where)

    1
    2
    3
    4
    5
    6
    fun <T> ensureTrailingPeriod(seq: T)
    where T : CharSequence, T : Appendable{
    if (!seq.endsWith('.')){
    seq.append('.')
    }
    }
  • same JVM signature problem

    1
    2
    // fun List<Int>.average(): Double {}
    // fun List<Double>.average(): Double {}

    Solution

    1
    2
    3
    fun List<Int>.average(): Double {}
    @JvmName("averageOfDouble")
    fun List<Double>.average(): Double {}

三. Conventions

1. Operator Overloading

1) Arithmetic operations

expression function name
a + b plus
a - b minus
a * b times
a / b div
a % b mod
1
2
3
4
5
6
7
8
9
operator fun Point.plus(other: Point): Point {
return Point(x + other.x, y + other.y)
}

fun main() {
val p1 = Point(3, 4)
val p2 = Point(5, 6)
println(p1 + p2) // (8,10)
}

2) No restrictions on parameter type

1
2
3
4
5
6
7
8
operator fun Point.times(scale: Int): Point {
return Point(x * scale, y * scale)
}

fun main() {
val p3 = Point(1, 2)
println(p3 * 3) // (3,6)
}

3) Unary operation

1
operator fun Point.unaryMinus() = Point(-x,-y)
Expression function name
+a unaryPlus
-a unaryMinus
!a not
++a,a++ inc
–a,a– dec

4) Assignment operations

  • a += b

    it you define any one of the 2, it will call the defined one,

    else if you define 2, you will call a.plusAssign if a is val,

    else you will get a compile error

    • a = a.plus(b)
    • a.plusAssign(b)

5) Conventions for lists

  • Prefer val to var
1
2
3
4
5
6
7
8
9
10
11
    var l1 = listOf(1, 2)                        
l1 += 2 // `l1 = l1.plus(2)` is called

val l2 = listOf(1, 2)
// l2 += 2 // compile error, val can't be resi

var l3 = mutableListOf(1, 2)
// l3 += 2 // compile error, ambiguous `+=`

val l4 = mutableListOf(1, 2)
l4 += 2 // `l4.plusAssign(2) is called`

2. Conventions

1) Comparisons

symbol translated to
a > b a.compareTo(b) > 0
a < b a.compareTo(b) < 0
a >= b a.compareTo(b) >= 0
a <= b a.compareTo(b) <= 0

2) Equality check

  • s1 == s2 calls equals under the hood s1.equals(s2)

  • it can correctly handles nullable values

    1
    2
    3
    s1 == s2 // s1.equals(s2)
    null == "abc" // false
    null == null // true

3) Index

  • Accessing elements by index: []

    x[a,b] // x.get(a,b)
    y[a,b] = c // x.set(a,b,c)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Board() {
    val cheet = Array(3) { CharArray(2) }
    operator fun get(x: Int, y: Int): Char {
    return cheet[x][y]
    }

    operator fun set(x: Int, y: Int, value: Char) {
    cheet[x][y] = value
    }
    }

    fun main() {
    val board = Board()
    board[1, 2] = 'x'
    board[1, 2] // 'x'
    }

4) Other convention

symbol translated to
a in c c.contains(a)
start..end start.rangeTo(end)
for(a in c){} (in a for loop) c.iterator()
  • Destructuring declarations

    val (a,b) = p

    • val a = p.component1()
    • val b = p.component2()
  • Destructuring in lambdas

    Description Syntax
    One parameter { a -> …}
    Two parameters { a, b -> …}
    A destructured pair { (a, b) -> …}
    A destructured pair and another parameter { (a, b), c -> …}
  • Iterating over list with index

    1
    2
    3
    for ((index, element) in list.withIndex()) {
    println("$index $element")
    }
    1
    2
    3
    4
    5
    for (indexedValue in list.withIndex()) {
    val index = indexedValue.component1()
    val element = indexedValue.component2()
    println("$index $element")
    }
  • Destructuring declarations & Data classes

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    data class Contact(
    val name: String,
    val email: String,
    val phoneNumber: String
    )

    fun main() {
    val contact = Contact("Kyle", "[email protected]", "185")
    contact.component1()
    contact.component2()
    contact.component3()

    val (name,_,phoneNumber) = contact
    }
作者

Kyle-Ye

发布于

2020-02-27

更新于

2020-02-27

许可协议