大三下总结

前言

从2月份短暂实习结束回到学校已经过了3个月了

希望借这篇博客回顾一下这3个月的学习生活

编程

Swift on Server Side

Book and Course

MachO Header & Disassembler

  • 了解MachO Header结构和dylib加载过程
  • 使用Hopper进行iOS反编译练习

推荐两个实用Demo

Apps

  1. WerewolfSimulation

    四月初去把和熊老师合作的论文项目结束了一下,修复了一下显示bug

    https://github.com/Kyle-Ye/WerewolfSimulation

    mac

    watch

  2. 大学生程序设计大赛

    1

    2

    3

  3. watermelon

    完善处理了合成大西瓜,并最后以此为codebase提交了WWDC21 Swift Student Challenge

    • iOS version
    • iPadOS version(支持120帧高刷模式)
    • macOS version(支持TouchBar和liveResize)
    • watchOS version(支持使用DigitalCrown进行操作)
    • 支持各版本之间的Handoff接力

    主要使用SwiftUI + SpriteKit 完成核心底层逻辑并被所有版本复用,然后为不同设备系统的交互层分开编码实现

    https://github.com/Kyle-Ye/watermelon (current private repo)

    watch

    TouchBar

  1. MangaHelper

    Apple 全平台漫画阅读器(支持Apple Watch),目前仅针对部分网站API进行了硬编码

  2. VideoHelper(Developing)

    一款可抓取不同平台视频并观看的视频辅助app(原bili-helper)

  3. Minesweeping(Developing)

    Mac上重新移植实现Windows经典扫雷游戏

    准备用NSDocument + AppKit 实现

    参考 https://apps.apple.com/cn/app/%E6%89%AB%E9%9B%B7-%E7%BB%8F%E5%85%B8%E7%89%88/id1475921958?mt=12 (缺陷:初始可能会踩雷,快速扫雷逻辑有bug)

Watch App 开发

参见李卓力大神的这篇的博客 WatchKit渲染原理以及SwiftUI桥接

这段时间其实做了不少Watch App的开发,虽然主要是用SwiftUI实现的,但鉴于SwiftUI的缺陷,有部分还是希望fall back到UIKit去做的,最后发现因为WatchOS 1的历史包袱,导致WatchKit在很多方面都是残废,本质是UIKit但是隐藏了大量的UIKit方法,最后还是只能用SwiftUI去曲折实现

比如对DigtalCrown的旋转回调,在WatchKit/UIKit中除了可以得到旋转角度外还可以在idle的时候得到通知

1
2
3
4
5
// SwiftUI
func digitalCrownRotation<V>(_ binding: Binding<V>)
// UIKit
func crownDidRotate(WKCrownSequencer?, rotationalDelta: Double)
func crownDidBecomeIdle(WKCrownSequencer?)

目标

  • 使用Vapor(Swift) + TailwindCSS 重构博客系统,替换原来的hexo引擎模版,自己造一波轮子,体会一下前/后端开发的快乐
  • 希望能更好地参与开源社区
    • 参与Swift Evolution,提出自己的一份proposal
    • 阅读 Blender 源码,添加 TouchBar Support

学业

大三下

这学期核心课程:信号与系统 + 计算机网络

4月份主要在准备考试周考试

6月份预计日语四级备考

虽然目前仍在保研线内(10%左右),但是应该是不准备读研了,大三下也没有为读研这方面做准备

目标

  • 6.12 日语四级考试
  • 暑假实习假期重温操作系统网课

工作

简历&面试

因为之前有过某厂的寒假短期实习,所以这次就只投了这家

用了内推码后只有一面,面了iOS这边很基础的Runloop和UIKit中一些底层知识,因为准备得不是很充分,平时UI拿SwiftUI写很少用UIKit,最后基本都没答上来,心态💥

不过最后还是被前Mentor捞了上来,希望这次到了之后好好表现

目标

  • Swift & OC 混编推进

  • XX for watchOS and macOS?

如果你想到了一个非常好的idea

别跟你的Mentor或者Leader说这东西多好多好

先一个人试着把它做到能让大家看到

这样所有人才能理解你的意图

💡停留在🧠里是毫无价值的

能切实地落地执行下来才有意义

游戏

第五人格

平时没课的时候除了学习就是沉迷第五人格了,然后就是3月底开始的深渊4中国大陆赛区和5月初的全球总决赛(这波上课去不了杭州看决赛,希望明年工作了可以五一有假期去线下应援一波🥺)

然后就是粉上了GG战队(🦐宝和小🐴哥)和狼队,5月份开始也会不定时在排位时间看他们的CC直播

目标

  • 平时玩第五人格时注意时间控制
  • 明年线下看深渊

生活

M1 Mac

实习两个月回来了打开Apple Store,选择了💰♻️计划,入了现在在用的这台M1 MBP,不得不说相较于以前使用的Y7000黑果,生产力和工作效率有了极大的提升,而且使用场景从以前的带电源游戏本变成了轻薄本,便携性也大大提高了,同时M1的续航真不是吹的,可以在外几乎工作一整天都不用🔋,总之这次的M1 Mac对我真的帮助不少。

锻炼计划

买了 ⌚️之后运动频率确实比以前提高了不少,但是还是希望能继续加油,保持每日晚跑好习惯

1 2

目标

  • 早睡早起(23:00 - 7:00)
  • 有时间中午/下午去健身房锻炼(华科健身房这学期到期了,应该不用去续了,反正目前实习的地方有健身房😊)
  • 一周3-5次晚跑🏃(22:00)
  • 睡前运动

总结

总的来说,这3个月的学习进度是差强人意的

希望这次暑假实习期能够继续提升自我,遇见更好的自己

Bili-helper 折腾记

Bili-helper 折腾记

前言

看不惯B站的夜间模式很久了,明明标榜深色跟随系统,结果居然需要手动杀掉后重启才会跟随

恰好学了一下Widget Kit,于是准备来一波demo,做一个iOS辅助客户端

准备

首先从这个 repo 了解了B站的基本API

开始

然后从认证开始入手

  • 一是让用户输入账户信息后,App模拟代为发送后收集保存返回的Cookie(主要是SESSDATA),但这样安全性不高,而且还要加入打标的前端js代码,遂放弃
  • 二是直接用类Safari原生网页组件,使用B站浏览器登录接口,然后拦截保存Cookie。
    • UIWebView已经被弃用,所以使用推荐的WKWebView,貌似最开始这玩意不太好获取Cookie,并且Cookie写入跨进程共享是异步的。不过最后发现后来加了一个webView.configuration.websiteDataStore.httpCookieStore.getAllCookies 可以用于获取
    • 然后在相应的生命周期中传递出去即可
    • 接下来是获取信息

      使用 URLSession.shared.dataTask 获取 data,最后用 JSONDecoder().decode 成相应的数据即可

decode时有时候有多余的父级数据,想了n多方法还是没解决(用SwiftyJSON库等),最后只能强行套一层BiliResult解决

设计UI

设计App UI和Widget UI

Widget信息同步

因为Widget同样要获得UserInfo等信息,需要使用保存的SESSDATA,故二者间需要同步信息
最后采用App Groups + UserDefaults(suiteName:)解决

Note: ⚠️suiteName需要和group id一致

Widget无法使用URLImage

因为Widget中EntryView一旦发送就不能再改变(如动画、图片切换),所以在Widget中不能使用URLImage

解决方案: 重新撸了一个getImageData(for url:URL),在Widget的View中直接使用Data加载图片

alternative: 使用 URLSession.shared.downloadTask 得到一个本地图片文件(url),使用contentOfUrl加载图片

源代码

https://github.com/Kyle-Ye/Bili-helper

Learn Git

Learn Git

最近在学 MIT 的 missing 课程,其中的 Git 课我觉得真的有必要听一下(当然其他课程也非常赞)

讲了 Git 的模型结构ss

git_1

git_2

model

Git 常用命令

  • Git blame filename

  • Git diff [name/id]? [name/id]? filename

  • Git checkout [name/id/filename]

    • if you checkout using a filename, that file will be like its origal state in the head snapshot
  • git add -p file

    • s(split)
    • y/n
  • git bisect

https://jwiegley.github.io/git-from-the-bottom-up

折腾虚拟机ssh

折腾虚拟机ssh

环境

Host OS:Mac 10.15.4

SoftWare:PD

OS:Arch

过程

  • 安装Arch
  • 安装openssh,并开启sshd服务
  • 关闭iptables防火墙,使用ssh连接虚拟机ip
    • 若失败,检查
      • 在虚拟机上ssh 127.0.0.1
      • 在虚拟机上ssh 虚拟机本地ip
    • 如果上面都可以,则多半是宿主机的问题
    • 最后经排查是宿主机的~/.ssh/config里面有一条全局代理设置导致错误

其他

  1. 虚拟机中使用宿主机的proxy
  • 将宿主机中将proxy的监听Host由127.0.0.1改为0.0.0.0
  • 在虚拟机中用宿主机本地ip:对应端口即可走代理

Parallels Desktop添加硬盘

参考链接 http://zhongce.sina.com.cn/article/view/25765/

前言

因为是三系统用户,经常会有在macOS下访问Windows的ntfs盘中文件的需求。

之前(指在Arch下)一直是通过修改/etc/fstab的方式或者手动umount再mount rw方式

1
2
3
4
5
6
7
## /etc/fstab

# Warning - this file should only be modified with vifs(8)
#
# Failure to do so is unsupported and may be destructive.
#
LABEL=WinData none ntfs rw,noauto
1
2
3
sudo umount /Volumes/WinData
sudo mkdir /Volumes/WinData
sudo mount -t ntfs -o rw,auto,nobrowse /dev/disk4s3 /Volumes/WinData

换用macOS后继续这样使用了一段时间,但是被黑果群的大佬警告这样强行在ntfs下写入数据可能导致数据全部丢失,于是使用PD转为更安全的Windows虚拟机下用读写

虽然Parallels Desktop虚拟机表现不错,可以明确的弊端是,Parallels Desktop仅支持已安装Windows的物理盘挂入虚拟机使用,原生不支持加入第二块甚至多块物理硬盘,如果你的物理Windows系统使用多块硬盘,那么就无法愉快的通过虚拟机玩耍了!

(理论上也可以,不过在虚拟机中看到和使用的不是原本的ntfs格式,而是PD在虚拟机专用的另一种格式)

添加Windows虚拟机

使用iso安装或者直接添加已有的Windows,略

因为之前已经有Windows安装在同一块SSD上,所以本人直接用了Parallels Desktop 添加Bootcamp中的Windows

环境准备

1.正在运行的macOS系统(本文使用版本Mojave 10.15.2)

2.已安装Parallels Desktop虚拟机软件(本文使用版本14.1.3-45485)

3.已安装好Windows的硬盘(本文使用Windows 10当前最新版本)

4.待添加到Parallels Desktop的第“二”块ntfs物理盘(已分区)(实际上任意格式都可以通过此方法添加)

实现步骤

见参考🔗

Week5

Week5

一. Inline functions

1. Libraray functions looking like build-in constructs

1) Useful library functions

  • run return the block of code (lambda) and returns the last expression as the result

  • let allows to check the argument for being non-null, not only the receiver

    1
    2
    3
    4
    5
    6
    7
    8
    fun main() {
    val email: Email? = getEmail()
    if (email != null) sendEmail(email)

    email?.let { sendEmail(it) }

    getEmail()?.let { sendEmail(it) }
    }

    Kotlin is not a pure functional language

    1
    2
    3
    4
    5
    6
    7
    8
    9
    fun analyzeUserSession(session: Session){
    val user = session.user
    if (user is FaceBookUser){
    println(user.accountId)
    }
    (session.user as? FaceBookUser)?.let {
    println(it.accountId)
    }
    }
  • takeIf returns the receiver object if it satisfies the given predicate, otherwise returns null

    1
    2
    3
    issue.takeIf { it.status == FIXED}

    person.patronymicName.takeIf(String::isNotEmpty)
    1
    2
    3
    4
    val number = 42
    println(number.takeIf { it > 10 }) // 42
    val other = 2
    println(other.takeIf { it > 10 }) // null

    Using takeIf in chained calls

    1
    2
    3
    issues.filter { it.status == OPEN }
    .takeIf(List<Issue>::isNotEmpty)
    ?.let { println("There're some open issues") }
  • takeUnless returns the receiver object if it does not satisfy the given predicate, otherwise returns null

  • repeat

    1
    2
    3
    4
    repeat(10)
    {
    println("Welcome!")
    }

All these functions are declared as inline functions (There is no performance overhead)

2. The power of inline

1) inline function

compiler substitutes a body of the function instead of calling it

1
2
3
4
5
6
7
8
9
10
11
fun myRun(f: () -> Unit) = f()
inline fun run(f: () -> Unit) = f()
fun main() {
val name = "Kotlin"
// brings performance overhead (class InlineKt$main$1 is created)
myRun { println("Hi, $name") }
// No performance overhead
run { println("Hi, $name") }
// in comparison to
println("Hi, $name")
}

inline constraints : You can’t postpone the call.

2) withLock function

3) Resource management: use function

No performance overhead when you use

run, let, takeIf, takeUnless, repeat, withLock, use

No anonymous class or extra objects are created for lambda under the hood

Note: If you call a inline function in Java, you won’t get it inlined.

Because it is credit to the Kotlin compile

4) @InlineOnly

Specifies that this function should not be called directly without inling

  • Not in your jar
  • Can’t be called in Java

Fin.

However, use inline in your code with care, because actually hotspot can do this job for you.

二. Sequences

1. Collections vs Sequences

1) Collections

  • example

    1
    2
    3
    4
    5
    6
    val list = listOf(1, 2, 3)
    val maxOddSquare = list
    .map { it * it }
    .filter { it % 2 == 1 }
    .max()
    // create 3 collections in total
  • Operations on collections

    • lambdas are inlined (no performance overhead)
    • But : intermediate collections are created for chained calls

Collections vs Sequences is like Eager vs lazy evaluation

2) Sequences

  • example

    1
    2
    3
    4
    5
    6
    7
    val list = listOf(1, 2, 3)
    val maxOddSquare = list
    .asSequence()
    .map { it * it }
    .filter { it % 2 == 1 }
    .max()
    // create 1 collections in total

2. More about Sequences

1) Test 1

  • fun test1() {
        listOf(1, 2, 3, 4)
            .map { it * it }
            .find { it > 3 } // 4
        // we don't do anything unless it is need
        listOf(1, 2, 3, 4)
            .asSequence()
            .map { it * it }
            .find { it > 3 } // 4
    }
    <!--code9-->
    
    ![截屏2020-01-2810.04.37](/images/Kotlin for Java Developers/Week5/2.png)
    
    ![截屏2020-01-2810.14.40](/images/Kotlin for Java Developers/Week5/3.png)
    
    -   intermediate operations return you another sequence
    -   terminal operations return you everything else
    

3) Test 3 Order of operations is important

  • fun test3() {
        fun m(i: Int): Int {
            print("m$i ")
            return i
        }
    
        fun f(i: Int): Boolean {
            print("f$i ")
            return i % 2 == 0
        }
    
        val list = listOf(1, 2, 3, 4)
        list.asSequence().map(::m).filter(::f).toList() // m1 f1 m2 f2 m3 f3 m4 f4
        list.asSequence().filter(::f).map(::m).toList() // f1 f2 m2 f3 f4 m4
    }
    <!--code10-->
    

![5](/images/Kotlin for Java Developers/Week5/5.png)

2) Generating a sequence

  • generateSequence{xx} : until xx return null

    1
    2
    3
    4
    5
    6
    fun test1() {
    val seq = generateSequence {
    Random.nextInt(5).takeIf { it > 0 }
    }
    println(seq.toList())
    }

3) Generating an infinite sequence

1
2
3
4
fun test2() {
val numbers = generateSequence(0) { it + 1 }
numbers.take(5).toList() // [0,1,2,3,4]
}

To prevent integer overflow

1
2
3
4
fun test3() {
val numbers = generateSequence(BigInteger.ZERO) { it + BigInteger.ONE }
numbers.take(5).toList() // [0,1,2,3,4]
}

4) Yield (lazy)

  • fun test5(){
        val numbers = sequence {
            var x =0
            while (true){
                yield(x++)
            }
        }
        numbers.take(5).toList() // [0,1,2,3,4]
    }
    <!--code14-->
    

4. Library Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
class Person(val age: Int, val name: String) {
val isPublicProfile: Boolean = false
}

fun main() {
val people = listOf(Person(1, "Kate"))

people.filter { it.age < 21 }.size
people.count { it.age < 21 }

people.sortedBy { it.age }.reversed()
people.sortedByDescending { it.age }

people
.map { person ->
person.takeIf { it.isPublicProfile }?.name
}
.filterNotNull()
people.mapNotNull { person ->
person.takeIf { it.isPublicProfile }?.name
}

people.filterNotNull().map { it.name }
people.mapNotNull { it?.name }

val map = mutableMapOf<Int, MutableList<Person>>()
for (person in people) {
if (person.age !in map) {
map[person.age] = mutableListOf()
}
val group = map.getValue(person.age)
group += person
}
for (person in people) {
val group = map.getOrPut(person.age) { mutableListOf() }
group += person
}
val mapOneWay = people.groupBy(Person::age)

people
.asSequence()
.groupBy { it.age } // not lazy
.mapValues { (_, group) -> group.size }
people
.asSequence()
.groupingBy { it.age }
.eachCount()
}

三. Lambda with Receiver

1. Lambda with Receiver

1) with function

1
2
3
4
5
6
val sb = StringBuilder()
sb.appendln("Alphabet: ")
for (c in 'a'..'z'){
sb.append(c)
}
sb.toString()
1
2
3
4
5
6
7
8
val sb = StringBuilder()
with(sb){
appendln("Alphabet: ")
for (c in 'a'..'z'){
append(c)
}
toString()
}
  • with(receiver: T, block: T.() -> R): R is a function

2) Lambda vs lambda with receiver

regular funtion regular lambda
Extension function lambda with receiver
1
2
3
4
5
6
7
8
9
fun test2() {
// lambda
val isEven: (Int) -> Boolean = { it % 2 == 0 }
isEven(0)

// lambda with receiver
val isOdd: Int.() -> Boolean = { this % 2 == 1 }
1.isOdd()
}
  • improvment:

    1
    2
    3
    4
    5
    6
    7
    8
    fun test3(){
    val s = buildString {
    appendln("Alphabet: ")
    for (c in 'a'..'z') {
    append(c)
    }
    }
    }
  • Html example

2. More useful library functions

  • with(receiver: T, block: T.() -> R): R

    1
    2
    3
    4
    5
    6
    7
    8
    fun withTest(){
    var window = Window()
    with(window){
    width = 300
    height = 200
    isVisible = true
    }
    }
  • T.run(block: T.() -> R): R

    { this.block(); return this }

    1
    2
    3
    4
    5
    6
    7
    8
    fun runTest(){
    val windowOrNull = windowById["main"]
    windowOrNull?.run {
    width = 300
    height = 200
    isVisible = true
    }
    }
  • T.let(block: (T) -> R): R

    { block(this); return this }

  • T.apply(block: T.() -> Unit): T

    { return this.block() }

    1
    2
    3
    4
    5
    6
    7
    8
    9

    fun applyTest() {
    val mainWindow =
    windowById["main"]?.apply {
    width = 300
    height = 200
    isVisible = true
    } ?: return
    }
  • T.also(block: (T) -> Unit): T

    { return block(this) }

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    fun alsoTest() {
    val mainWindow =
    windowById["main"]?.apply {
    width = 300
    height = 200
    isVisible = true
    }?.also {
    showWindow(it)
    }
    }
{ .. this .. } { .. it .. }
return result of lambda with / run let
return receiver apply also

四. Types

1. Basic types

1) Bytecode of Kotlin

  • Kotlin code (types.kt)

    1
    2
    fun foo(): Int = 1
    fun bar(): Int? = 1
  • Decompiled Java code (TypesKt.java)

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    public final class TypesKt {
    public static final int foo() {
    return 1;
    }

    @Nullable
    public static final Integer bar() {
    return 1;
    }
    }

2) Kotlin Bytecode

  • Primitive & wrapper types

    Kotlin Java
    Int int
    Int? Java.lang.Integer

The same for double and boolean

  • Generic arguments

    Kotlin Java
    List<Int> List<Integer>
  • Arrays of primitive types

    Kotlin Java
    Array<Int> Integer[]
    IntArray int[]
  • String

    Kotlin java
    kotlin.String Java.lang.String

    Kotlin.String modifies some of Java String’s API

    • such as String.replaceAll

      1
      2
      3
      // Java
      "one.two.".replace(".", "*"); // one*two*
      "one.two.".replaceAll(".", "*"); // ********
      1
      2
      3
      // Kotlin
      "one.two.".replace(".", "*") // one*two*
      "one.two.".replace(".".toRegex(), "*") // ********
  • Any

    Kotlin Java
    Any Java.lang.Object

    Any in Kotlin is a super class type for all non-nullable types (Both for reference type and for primitive type)

    • Boxing under the hood

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      log(2017) 

      // 2017 will be boxing into Integer
      fun log(any: Any) {
      println("Value: $any")
      }

      // 2017 will not be boxing
      fun log(i: Int) {
      println("Value: $i")
      }
  • Function types

    Kotlin Java
    () -> Boolean Function0<Boolean>
    (Order) -> Int Function1<Order, Int>
    (Int, Int) -> Int Function2<Int, Int, Int>

3) Prefer Lists to Arrays

  • If you write pure Kotlin, use list
  • If you write Kotlin with Java, use array

2. Kotlin type hierarchy

![6](/images/Kotlin for Java Developers/Week5/6.png)

1) Unit vs Nothing vs void

  • Unit instead of void

    Kotlin Java
    Unit void
  • Nothing is defferent to Unit/void

    it means “this function never returns”

    1
    2
    3
    fun fail(message: String): Nothing {
    throw IllegalArgumentException(message)
    }

    ![截屏2020-01-2815.23.18](/images/Kotlin for Java Developers/Week5/7.png)

  • Expressions that have Nothing type :

    throw IllegalArgumentException return TODO("Needs to be done")

2) Nothing

![截屏2020-01-2815.34.32](/images/Kotlin for Java Developers/Week5/nothing-1.png)

![截屏2020-01-2815.34.32](/images/Kotlin for Java Developers/Week5/nothing-2.png)

Kotlin Java
Nothing void

![截屏2020-01-2815.38.46 (/Volumes/MacWorkplace/Workplace/Kotlin/Kotlin for Java Developers/docs/Week5/nothing-3.png)](/Users/kyle/Desktop/截屏2020-01-2815.38.46 (2).png)

  • the simplest expression of Nothing? type: null

3) Type of null

1
2
var user = null // user is inferred as Nothing?
val users = mutableListOf(null) // users is inferred as MutableList<Nothing?>

3. Nullable types

Java Kotlin
@Nullable Type Type?
@NotNull Type Type
Type Type! (Notation, not syntax)

Type that came from Jave -> type of “unknown” nullability in Kotlin

1) Type! in Kotlin

  • 1
    2
    3
    4
    5
    6
    // Java
    public class Session {
    public String getDescription() {
    return null;
    }
    }
  • IllegalStateException

    1
    2
    3
    4
    5
    fun test1(){
    val session = Session()
    val description:String = session.description // exception
    println(description.length)
    }
  • NullPointerException

    1
    2
    3
    4
    5
    fun test2(){
    val session = Session()
    val description = session.description // description is inferred as `String!`
    println(description.length) // exception
    }
  • (Correct) use ?. to safe access

    1
    2
    3
    4
    5
    fun test3() {
    val session = Session()
    val description = session.description // description is inferred as `String!`
    println(description?.length)
    }
  • (Correct) to prevent test2, if you use .length, you’ll get compile error

    1
    2
    3
    4
    5
    fun test4() {
    val session = Session()
    val description: String? = session.description // description is inferred as `String!`
    println(description?.length)
    }

2) How to still prevent NPEs?

  • Annotate your Java types

    Different annotations are supported

    @Nullable @NotNull JetBrains
    @Nullable @NotNull Android
    @Nullable @CheckForNull JSR-305
    @Nullable @CheckForNull FindBugs
    @NonNull Lombok
    1
    2
    3
    4
    5
    6
    7
    // Java
    public class Session {
    @Nullable
    public String getDescription() {
    return null;
    }
    }
    1
    2
    3
    val session = Session()
    val description = session.description // description is inferred as `String?`
    println(description?.length)

    Tip: make one annotation as default, only specify the other as needed

  • Specify types explicitly

    1
    2
    3
    4
    5
    6
    // Java
    public class Session {
    public String getDescription() {
    return null;
    }
    }
    1
    2
    3
    val session = Session()
    val description: String? = session.description // description is inferred as `String!`
    println(description?.length)
    1
    2
    3
    val session = Session()
    val description:String = session.description // get IllegalStateException exception immediately, better than NPE
    println(description.length)

4. Collection types

1) Standard collections

1
2
3
4
5
6
val set = hashSetOf(1,7,53)
val list = arrayListOf(1,7,53)
val map = hashMapOf(1 to "one")
println(set.javaClass) //class java.util.HashSet
println(list.javaClass) // class java.util.ArrayList
println(map.javaClass) // class java.util.HashMap

2) (Read-only)List & MutableList

  • 2 interfaces declared in kotlin.collections package

  • MutableList extends List

Note: Read-only $\neq$ immutable

  • Read-only interface just lacks mutaing methods

  • The actual list can ve changed by another reference

  • See example below

    1
    2
    3
    4
    5
    6
    fun readOnlyList(){
    val mutableList = mutableListOf(1, 2, 3)
    val list: List<Int> = mutableList
    mutableList.add(4)
    println(list) // [1, 2, 3, 4]
    }
  • Under the hood, both List and MutableList is the same java.util.List in bytecode

![截屏2020-01-2817.26.21](/images/Kotlin for Java Developers/Week5/list.png)

3) Read-only interfaces improve API

1
2
3
4
5
6
7
8
9
10
11
class Customer

object Shop{
private val customers = mutableListOf<Customer>()
fun getCustomers():List<Customer> = customers
}

fun main(){
val customers = Shop.getCustomers()
// customers.add() // compile error
}

If you want, you can forcely turn List into MutableList, but it avoids you from accidently misusing.

Summary: Good compromise between safety and convenience

Readme

介绍

该系列为Coursera网课 Kotlin for Java Developers笔记

Week2

Week 2

From Java to Kotlin

Basics

1
2
3
const val a = 3
val b = 4
var c = 5
1
2
3
private static final int a = 3;
private final int b =4;
private int c =5

Control Structures

Extensions

1. Extension Functions

Accessing private members

  • In Java you can’t call a private member from a static function of another class
  • Kotlin extension functions are regular static functions defined in a separate auxiliary class.
  • You can’t call private members from extensions

2. Examples from the Standard Library

Kotlin standard library = Java standard library + extensions

infix form

1
2
3
4
5
6
7
8
9
10
public infix fun Int.until(to: Int): IntRange {
if (to <= Int.MIN_VALUE) return IntRange.EMPTY
return this .. (to - 1).toInt()
}
public infix fun <A, B> A.to(that: B): Pair<A, B> = Pair(this, that)

1.until(9)
1 until(9)
// infix form
1 until 9

3. Call Extensions

No override in extension

1
2
3
4
5
6
7
8
9
10
open class Parent
class Child:Parent()

fun Parent.foo() = "parent"
fun Child.foo()="child"

fun main(){
val parent: Parent = Child()
println(parent.foo())
}

因为extensive function在底层是static实现的,所以不存在重载

member vs extension

member > extension

member always wins!!

1
2
3
4
5
6
7
8
9
class A {
fun foo()=1
}
fun A.foo() =2
fun main(args: Array<String>) {
val a = A()
println(a.foo())
// "1" will be printed
}

4. Importance of extensions

在类的定义中只保留核心本质,将扩展API放入extension中

eg. String is a CharSequence with length n. Other APIs like replece, split are all its extensions.

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
    }

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-->