Kotlin语言基础入门

Posted 红日666

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Kotlin语言基础入门相关的知识,希望对你有一定的参考价值。

一、变量:

Kotlin中定义一个变量,只允许在变量前声明两种关键字:val var

1、val(value):声明常量

2、var(variable):声明变量

二、函数:

Kotlin函数的语法规则:

fun methodName(param1: Int, param2: Int): Int 
    return 0

注:如果函数不需要返回数据,则括号后面的部分可省略。

当函数只有一行代码时,可以这么写:

fun largeNumber(num1: Int, num2: Int): Int = max(num1, num2)

然后,根据Kotlin的类型推导机制,上述函数可进一步简化为:

fun largeNumber(num1: Int, num2: Int) = max(num1, num2)

三、面向对象编程:

1、类与对象:

声明一个类:

Kotlin中也是使用class关键字来声明一个类。

实例化一个类:

Kotlin中实例化一个类的方式和Java基本类似,只是去掉了new关键字

2、继承和构造函数:

  • 要想一个类(如Student)继承另一个类(如Person),我们得做两件事才可以:

<1> 使Person类可以被继承,即需在类前面添加open关键字。因为在Kotlin中默认非抽象类都是不可以被继承的。

open class Person 
    ...

<2> 继承使用 : 代替Java的extends关键字。

class Student : Person() 
    ...
  • kotlin将构造函数分成了两种:主构造函数和次构造函数:

主构造函数:

主构造函数是最常用的构造函数,每个类默认都会有一个不带参数的主构造函数,当然也可以显式地给它指明参数。主构造函数的特点是没有函数体,直接定义在类名的后面即可。

class Student(val name: String, val age: Int) : Person() 
    ...

由于主构造函数没有函数体,如果想在主构造函数中编写一些逻辑,可以写到init结构体中。

//在这里,Person类后面的一对空括号表示Student类的主构造函数在初始化的时候回调用Person类的无参构造函数。
class Student(val name: String, val age: Int) : Person() 
    init 
        println("name is:" + name)
        println("age is:" + age)
    

次构造函数:

任何一个类只能有一个主构造函数,但是可以有多个次构造函数。Kotlin规定,当一个类中既有主构造函数又有次构造函数时,所有的次构造函数都必须调用主构造函数(包括间接调用)。次构造函数时通过constructor关键字来定义的。

class Student(val name: String, val age: Int, sno: String, grade: Int) : Person(sno, grade) 
    constructor(sno: String, grade: Int): this("", 18, son, grade) 
        ...
    
    constructor() : this("", 0)
        ...
    

Kotlin允许类中只有次构造函数,没有主构造函数:

class Student : Person() 
    //由于没有主构造函数,次构造函数只能直接调用父类的构造函数
    constructor(name: String, age: Int) : super(name, age) 
        ...
    

3、接口:

Kotlin中实现接口的关键字也是:,中间用逗号进行分隔。

interface Study 
    fun readBooks()

    //允许对接口中定义的函数进行默认实现
    fun doHomework() 
        pringln("do homework default implementation.")
    


class Student(name: String, age: Int) : Person(name, age), Study 
    override fun readBooks() 
        println(name + " is reading.")
    

Kotlin有四种可见修饰符:

public:对所有类都可见。但在kotlin中public修饰符是默认项,而在Java中default才是默认项。

private:跟Java的private一样,只对当前类内部可见。

protected:kotlin中表示只对当前类和子类可见。而在Java中表示对当前类、子类和同一包路径下的类可见。

internal:只对同一模块中的类可见。kotlin抛弃了Java中的default可见性(同一包路径下的类可见)。

4、数据类与单例类:

数据类:

//当在一个类前面声明了data关键字时,就表明你希望这个类是数据类,Kotlin会根据主构造函数中的参数帮你
//将equals()、hashCode()、toString()等固定且无实际逻辑意义的方法自动生成。
data class Cellphone(val brand: String, val price: Double)

单例类:

在kotlin中创建一个单例类的方式及其简单,只需要将class改成object关键字即可:

object Singleton 
    fun singletonTest() 
        println("singletonTest is called")
    

四、Lambda编程:

1、集合的创建与遍历:

Kotlin专门提供了一种内置的listOf()函数来初始化集合:

val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape")

需要注意的是listOf()函数创建的是一个不可变集合

如果需要创建一个可变的集合,可以使用mutableListOf()函数:

val list = mutableListOf("Apple", "Banana", "Orange", "Pear", "Grape")
list.add("Watermelon")

Set集合用法类似,setOf()与mutableSetOf()分别表示不可变与可变Set集合。

同样,Map集合函数中,mapOf()和mutableMapOf()函数分别表示不可变与可变Map键值对集合:

mapOf()函数:

val map = mapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)
for((fruit, number) in map) 
    println("fruit is " + fruit + ", number is " + number)

mutableMapOf()函数: 

val map = mutableMapOf("Apple" to 1, "Banana" to 2, "Orange" to 3, "Pear" to 4, "Grape" to 5)
//以下是两种map添加数据的方式
map.put("Peach", 6)
map["Watermelon"] = 7
for((fruit, number) in map) 
    println("fruit is " + fruit + ", number is " + number)

2、集合的函数式API:

Lambda的(直白)定义:

Lambda就是一小段可以作为参数传递的代码。

Lambda表达式的语法结构:

参数名1: 参数类型, 参数名2: 参数类型 -> 函数体

Lambda表达式可以从 list.maxBy it.length 方法来说起。即求list集合中单词最长的那个水果:

val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val maxLengthFruit = list.maxBy  it.length 
println("max length is " + maxLengthFruit)

上面这种写法方式是如何生成的呢,下面一步步进行分析:

  • maxBy函数实质上是接收了一个Lambda参数,根据上面定义的Lambda表达式的语法结构,可得:
val lambda =  fruit: String -> fruit.length 
val maxLengthFruit = list.maxBy(lambda)

即: 

val maxLengthFruit = list.maxBy( fruit: String -> fruit.length )
  • Koltin规定,当Lambda参数是函数的最后一个参数时,可以将Lambda表达式移到函数括号的外面:
val maxLengthFruit = list.maxBy()  fruit: String -> fruit.length 
  • 接下来,如果Lambda参数时函数的唯一一个参数的话,还可以将函数的括号省略:
val maxLengthFruit = list.maxBy  fruit: String -> fruit.length 
  • 由于Kotlin拥有出色的类型推导机制,Lambda的表达式中的参数列表其实在大多数情况下不必声明参数类型:
val maxLengthFruit = list.maxBy  fruit -> fruit.length 
  • 当Lambda表达式的参数列表中只有一个参数时,也不必声明参数名,而是可以使用it关键字来代替。所以最终代码就变成了最开始列出的list.maxBy it.length
val maxLengthFruit = list.maxBy  it.length 

map函数:

集合中的map函数是最常用的一种函数式API,它用于将集合中的每个元素都映射成一个另外的值:

val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val newList = list.map  it.toUpperCase() 

 filter函数:

filter函数是用来过滤集合中的数据的,它可以单独使用,也可以结合map函数使用:

val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
//过滤出单词中小于等于5的单词,并转换为大写
val newList = list.filter  it.length <=5 .map  it.toUpperCase() 

any函数和all函数:

any函数用于判断集合中是否至少存在一个元素满足指定条件;all函数用于判断集合中是否所有元素都满足指定条件:

val list = listOf("Apple", "Banana", "Orange", "Pear", "Grape", "Watermelon")
val anyResult = list.any  it.length <= 5  //true
val allResult = list.all  it.length <= 5  //false

3、Java函数式API的使用:

如果我们在Kotlin代码中调用了一个Java方法,并且该方法接收一个Java单抽象方法接口【接口中只有一个待实现方法】参数,就可以使用函数式API:

Java代码创建并执行一个子线程:

new Thread(new Runnable() 
    @Override
    public void run() 
        System.out.println("Thread is running")
    
).start()

Kotlin版本如下:

//由于Kotlin完全舍弃了关键字new,因此创建匿名类实例的时候就不能再使用new了,而是改用了object关键字
Thread(object: Runnable 
    override fun run() 
        println("Thread is running")
    
).start()

由于目前Thread类的构造方法是符合Java函数式API的使用条件的,所以可做精简:

//因为Runnable类中只有一个待实现方法,即使这里没有显示的重写run()方法,Koltin也能自动明白Runnable后面的Lambda表达式就是要在run()方法中实现的内容
Thread(Runnable 
    println("Thread is running")
).start()

如果一个Java方法的参数列表中不存在一个以上Java单抽象方法接口参数,我们还可以将接口名进行省略:

Thread(
    println("Thread is running")
).start()

当Lambda表达式是方法的最后一个参数时,可以将Lambda表达式移到方法括号的外面。同时,如果Lambda表达式还是方法的唯一一个参数,还可以将方法的括号省略:

Thread 
    println("Thread is running")
.start()

五、空指针检查:

1、可空类型系统:

Java语言的NPE问题一直是个令人头疼的问题。然而,Kotlin却非常科学地解决了这个问题,它利用编译时判空检查的机制几乎杜绝了空指针异常。

也就是说,Kotlin将空指针异常的检查提前到了编译时期,如果我们的程序存在空指针异常风险就会出现编译出错,只有修正之后才能成功运行。

另外Kotlin提供了一套可为空的类型系统【用于支持业务中真的需要某个变量或参数为空的情况】,就是在类名的后面加上一个问号。如String表示不可为空的字符串,而String?表示可为空的字符串。

2、判空辅助工具:

  • ?.操作符

当对象不为空时正常调用相应的方法,为空时什么都不做。

if(a != null) 
    a.doSomething()

//以上代码使用?.操作符可以简化成:
a?.doSomething()
  • ?:操作符

该操作符的左右两边都接收一个表达式,如果左边的表达式不为空就返回左边表达式的结果,否则就返回右边表达式的结果。

val c = if(a != null) 
    a
 else 
    b

//以上代码使用?:操作符可简化为:
val c = a ?: b

?.操作符与?:操作符结合使用举例:

fun getTextLength(text: String?): Int 
    if(text != null) 
        return text.length
    
    return 0


//以上代码可简化为:
fun getTextLength(text: String?) = text?.length ?: 0
  • !!操作符

是一种非空断言工具,告知Kotlin确信此处不会为空,所以不用Kotlin再帮做空指针检查了。这是有风险的一种写法,慎用。

val content: String? = "hello"

fun printUpperCase() 
    val upperCase = content!!.toUpperCase()
    println(upperCase)
  • let函数配合?.

let函数提供了函数式API的编程接口,并将原始调用对象作为参数传递到Lambda表达式中:

这里调用了obj对象的let函数,然后Lambda表达式中的代码就会立即执行,并且这个obj对象本身还会作为参数传递到Lambda表达式中。这里obj和obj2是同一个对象。
obj.let  obj2 ->
    //编写具体的业务逻辑

let函数结合?.操作符举例: 

//?.操作符表示对象为空时什么都不做,对象不为空时就调用let函数,而let函数会将study对象本身作为参数传递到Lambda表达式中,此时的study对象肯定不为空了。
fun doStudy(study: Study?) 
    study?.let  stu ->
        stu.readBooks()
        stu.doHomework()
    

当Lambda表达式的参数列表中只有一个参数时,可以不用声明参数名,直接使用it关键字来代替即可:

fun doStudy(study: Study?) 
    study?.let 
        it.readBooks()
        it.doHomework()
    

另外let函数是可以处理全局变量的判空问题的,而if判断语句则无法做到这一点。

var study: Study? = null

fun doStudy() 
    if(study != null) 
        //编译时这里会报错
        study.readBooks()
        study.doHomework()
    

六、Kotlin使用小技巧:

1、字符串内嵌表达式:

kotlin允许我们在字符串里嵌入$这种语法结构的表达式,并在运行时使用表达式执行的结果替代这一部分内容:

"hello, $obj.name. nice to meet you!"

当表达式中仅有一个变量是,可以将省略:

"hello, $name. nice to meet you"

2、函数的参数默认值:

我们在定义函数的时候可以给任意参数设定一个默认值,当调用此函数时就不会强制要求调用方为此参数传值,在没有传值的情况下回自动使用该参数的默认值:

fun playMovie(num: Int, name: String = "梁朝伟") 
    println("num is $num, name is $name")
 
//调用1 -- name会打印默认值
playMovie(7788)
//调用2 -- name会打印金城武
playMovie(7799, "金城武")

以上情况正好是最后一个参数设置了默认值,如果不是这种情况,应该如何传参呢?

fun playMovie(num: Int = 8899, name: String) 
    println("num is $num, name is $name")
 
//调用1 -- 编译报错,因为String跟首个参数类型Int无法匹配
playMovie("金城武")

Kotlin提供了一种机制,即可以通过键值对的方式来传参:

//调用2 -- 键值对传参 编号打印默认值
playMovie(name = "金城武")
//调用3 -- 编号打印7799
playMovie(num = 7799, name = "金城武")

参考:

《第一行代码》第3版

以上是关于Kotlin语言基础入门的主要内容,如果未能解决你的问题,请参考以下文章

号称取代 Java 的 Kotlin 语言到底是什么鬼?

Android:Kotlin详细入门学习指南-函数-基础语法

Kotlin基础Kotlin快速入门

Google 力挺 Kotlin,这是怎么回事!

kotlin入门---构建工具(下)

Android:Kotlin详细入门学习指南-基础语法