美文网首页Kotlin开发指南Kotlin专题Kotlin编程
介绍Kotlin第二部分(翻译篇)

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

作者: Kotyo | 来源:发表于2017-09-17 20:13 被阅读43次

    前言

    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的集合空安全类型安全,到这里第二部分的内容也算是告一段落了。

    快乐工作,享受编程!

    相关文章

      网友评论

        本文标题:介绍Kotlin第二部分(翻译篇)

        本文链接:https://www.haomeiwen.com/subject/vgxksxtx.html