介绍Kotlin第二部分(翻译篇)

前言

Kotlin介绍:第一部分,我们介绍了基本语法,现在我们可以去看看实际上如何使用Kotlin。在这篇文章中,我们将介绍collectionslambdas表达式,一些方便的扩展函数(applyletrunwith),null safety(空安全),那下面咱就开始吧。

1、Collections and Lambdas

那么Kotlin collections是什么呢?如果您熟悉Java8,您将会对这些collection方法(java流)和语法十分了解。然而,Kotlin提供了大部分你可能想得到的扩展,让我们一起来看看吧。

listOf(1,2,3)
mutableListOf("a", "b", "c")
 
setOf(1,2,3)
mutableSetOf("a", "b", "c")
 
mapOf(1 to "a", 2 to "b", 3 to "c")
mutableMapOf("a" to 1, "b" to 2, "c" to 3)

这些是基础,Kotlin为您提供了方法来创建collections,我在这儿列出了不可变和可变版本的List,Set和Map。Kotlin系列的编程除了默认的不变性外,还来自于Kotlin stdlib的扩展功能。如果您熟悉函数式编程,那么您将熟悉大部分功能。它们是一组辅助函数和更高级的辅助函数,可以为您的集合提供常用操作。有了这些扩展函数(mapflatMapforEachfoldreducefilterzip,...)很多操作完成起来就很方便。
在我们使用它们之前,我们需要先说一下lambdas表达式。Kotlin标准库的collection扩展功能的优点来自于易使用lambdas表达式,只需使用足够的类型推理来保证编程安全。在Kotlin中有几种方法来定义lambdas函数。

val aList = listOf(1,2,4)
aList.map { elem ->
    elem + 1
} // 2,3,5
 
aList.filter { it != 1} // 2,4
 
fun folder(a: Int, b: Int) = a + b
aList.reduce(::folder) // 7
// 或者: aList.reduce { a, b -> folder(a, b) }

在第一个例子中,我们定义了Kotlin lambdas的最常见用法。我们可以用角括号(->)来缩写匿名函数,我们可以改变lambdas参数的名称(在这里我们省略了类型定义;我们可以从aList列表中看到它是一个Int),然后我们定义lambda体,不需要使用return语句,最后一行将被返回。

下一个例子进一步说明,甚至可以省略参数定义。在Kotlin中,默认情况下,一个参数lambdas会接收到一个名为it的参数名。没有必要去命名它。请注意,如果过多的使用it,尤其在嵌套函数中,会导致代码非常混乱!

最后一个向我们展示了几个新的概念,首先是一个本地函数,我们引用了::一个双汇语法,本地函数的样式和作用类似于类或全局作用域函数,但还有一个额外功能,它还能访问与函数本身在同一范围定义的变量。引用本地函数的第二种方法我们将它称为内部lambda,就像注释中显示的那样。

正如你所看到的,Kotlin中的lambdas是以直截了当的方式定义的。它们在您的代码中也很明显,并使得高阶函数的使用变得简单。关于Kotlin和lambdas的最好部分是类型推断,当类型不匹配时,它就在你的代码下面出现一条红色的线。通过编译器的这种帮助,您可以将精力放在业务逻辑上,而不是试图找出循环应该遍历多少遍。

有关Kotlin的collection扩展功能的更多信息可以在官方网站API doc中找到

2、Null safety(空安全)

当涉及到可空性,Kotlin编译器会非常严格的剖析您的代码。如果定义一个可能为null的变量,则需要将其定义为可空。那这该怎么写呢?

var nil: String? = null
val notNil: String = "Hi"
var nil = null

这三个变量声明有两个可空值,一个不为null。无效性的共同点是问号;可空变量和函数参数用问号定义。这个问号在Kotlin的null safe起着重要的作用。如果Kotlin编译器在变量声明或函数参数/返回类型中看到这个问号,它将强制您对空检查。如果您主要编写的是Kotlin代码,那您将会从NullPointException解放出来。然而Kotlin与Java高度互操作,当你传入的数据可能为空时。Kotlin会让你处理这个十亿美元的错误

data class Lad(val name: String, val age: Int)
fun doSomething(laddy: Lad?){
    print(laddy.name)
}

如果您尝试这么做,Kotlin会编译器将会给出提示。在android studio中,您将得到文本下方的红色波浪线,它会给出Only safe (?.) or non-null asserted (!!.) calls are allowed on a nullable receiver of type Lad?。为了解决这个问题,你别无选择。

fun doSomething(laddy: Lad?){
    if(laddy != null){
        print(laddy.name)
    }
}
 
fun doSomething(laddy: Lad?){
    print(laddy?.name)
}
 
fun doSomething(laddy: Lad?){
    laddy?.name?.let {
        print(it)
    }
    /** 或者
    * laddy?.name?.let { name ->
    *     print(name)
    * }
    **/
}

第一个例子是之前的写法,正确的非空检查。编译器知道,在完成null检查之后,就可以使用我们的变量,红色波浪线就会从print语句中消失。在第二个例子,我们熟悉的问号再次出现了,但是这一次担任是不同的角色。在这方面,问号会提示If laddy is not null, then take the name property from it。如果laddy为空,那么null将会打印到控制台。

第三个介绍了一个扩展功能,我们可以用它来调用let。如果我们想从我们的函数返回一些东西,我们可以使用elvis作为默认值,以防我们碰到一个null。使用elvis有点像这样:

fun doSomething(laddy: Lad?) = laddy?.name?: "James"

当laddy和name都不为空时,才会返回“James”。

3、扩展功能(Apply, Let, Run, and With)

Kotlin推出了一些扩展功能,可以帮助我们处理。我们看到的第一个let是一个扩展,它将一个lambda作为参数。在上面的例子中,it意味着我们的对象属性name,但仅当laddy和name不为空时有效。let只对存在的东西有用,作为扩展功能,它不能扩展不存在的东西。

Apply是另一个时髦的扩展功能,我们可以在很多情况下使用它,一个常见的用法的就是创建一个需要许多调用的对象,但是没有很好的方法来做到这一点。为了简单起见,我们能想到JavaBean及其getter和seeter。

public class JavaBeanClass {
   private String thing;
   private String thang;
 
   public String getThing() {           
       return thing;                    
   }                                    
                                        
   public void setThing(String thing) { 
       this.thing = thing;              
   }                                    
                                        
   public String getThang() {           
       return thang;                    
   }                                    
                                        
   public void setThang(String thang) { 
       this.thang = thang;              
   }                                    
}

这看起来有点繁琐,没关系,让我们使用Kotlin看看。

val mrBean = JavaBeanClass().apply {
    setThing("Wild")
    setThang("erbeest")
}

这就很舒服了,其实在Kotlin中,还可以有其它的写法,与上述相同的代码还可以这么写:

val mrBean = JavaBeanClass().apply {
    thing = "Wild"
    thang = "erbeest"
}

这样就更简洁了。

接下来我们介绍with,这个家伙类似apply,实际上它不是一个扩展函数,它只是一个函数,接受了两个参数。我们来看一个例子,我们将使用与mrBean之前定义的相同的方法。

with(mrBean) {
    thing = "the"
    thang = "ain't no"
}

和apply非常相似,你不觉得吗?其实根本不一样,那是因为我们没有做任何事,with返回with块中最后一个表达式的值。这是一个重要的区别,所以让我们看一个更好的例子。

val yo = with(mrBean) {
    thang + "thing"
}
print(yo) // ain't nothing

我们继续看下一个操作符run,这是一个很简单的小东西。它是一个扩展函数,它接受一个参数,一个lambda。它只是调用该lambda并返回该lambda的响应。“那么这个家伙有什么用呢?” “你可能会问”。使用它来运行某些东西,当且仅当它被调用的对象不是null(使用它类似于let上面的几行,但在run这种情况下this作为范围的对象)或使用它来调用我们的函数调用并保护我们的lambdas。我们必须记住,做run同样的事情,但with通常更容易使用。

4、类型: Checking, casting, and safety(检查,转换,安全)

在Java世界中,您可能会遇到这样的if检查if (clazz instanceOf SomeClass)程序员希望看到他们是否正确实现其接口或扩展的基类。
在Kotlin中类型推断是非常好的,编译器在编写代码时给出了很多有用的提示。当您需要检查对象是否是某种类型时,您可以使用is关键字。

fun tryAndFailToCompileToGetTheAnswer(plzPassInThirteen: Any): Int {
    return plzPassInThirteen + 29
}
 
fun getTheAnswer(plzPassInThirteen: Any): Int {
    if (plzPassInThirteen is Int) {
        return plzPassInThirteen + 29
    }
    return 666
}
println(getTheAnswer(13)) // 42

在上面的代码块中,第一个函数将会失败,并且根本没有实际编译,它会报错,找不到类型匹配。第二个功能修复了:它做了一个简单的is检查,在这一点上,Kotlin智能的将该值转换为Int,因此它可以在if语句中使用。通常当when和is配合使用时,您可以这么写:

fun getTheAnswer(plzPassInThirteen: Any): Int = when(plzPassInThirteen) {
    is Int -> plzPassInThirteen + 29
    else -> 666
}
println(getTheAnswer(13)) // 42

这个例子与以前看到的if语句是一样的,但这不是更美观吗?

现在我们接触了iswhen在一起,现在我们可以绕个弯子谈一谈sealed classes,Kotlin有一个sealed classes的概念,我们可以把它当成一些子类的包装。

sealed class Seal
class SeaLion: Seal()
class Walrus: Seal()
class KissFromARose(val film: String): Seal()

如果我们有这样的结构,一个密封的超类和三个继承的子类,我们可以很好的处理多态和when以及is的组合。

fun getTheAnswer(songOrAnimal: Seal): Unit = when(songOrAnimal) {
    is SeaLion -> println("Animal")
    is Walrus -> println("Song by Beatles")
}

这是编译不过去的,编译器会告诉我们when中的声明少了哪一个子类,如果我们将KissFromARose添加上就不会出现问题。

fun getTheAnswer(songOrAnimal: Seal): Unit = when(songOrAnimal) {
    is SeaLion -> println("Animal")
    is Walrus -> println("Song by Beatles")
    is KissFromARose -> ("Heidi Klum")
}
println(getTheAnswer(Walrus())) // Song by Beatles

上面的编译就没什么问题,

有时候我们需要类型的转换,在Kotlin中,使用as关键字。当它被赋值时,我们可以假设它被转换为该类型,

val possiblyString: Any = "definitely"
possiblyString.capitalize()

上面的例子是无法编译的,capitalize()会有错误下划线,编译器告诉我们有一个Unresolved reference和resolver type mismatch。这个提示是对的,我们知道Any没有capitalize()方法,修改这个是容易的,我们只要将变量变成String就没问题了。

val possiblyString: Any = "definitely"
possiblyString as String
possiblyString.capitalize()

现在我们已经了解了Kotlin的集合空安全类型安全,到这里第二部分的内容也算是告一段落了。

快乐工作,享受编程!
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容