Kotlin核心编程知识点-01-基础语法

文章目录

  • 0.前言
  • 1.不一样的类型声明
    • 1.1.增强的类型推导
    • 1.2.声明函数返回值类型
    • 1.3.是否需要显示声明类型?
  • 2.val和var的使用规则
    • 2.1.val的含义:引用不可变
  • 3.高阶函数和Lambda
    • 3.1.抽象和高阶函数
    • 3.2.函数作为参数的需求
    • 3.3.函数的类型
    • 3.4.方法和成员引用
    • 3.5.匿名函数
    • 3.6.Lambda表达式
    • 3.7.函数、Lambda
  • 4.面向表达式编程
    • 4.1.枚举类和when表达式
      • 4.1.1.枚举是类
      • 4.1.2.用when代替if-else
      • 4.1.3.when表达式具体语法
    • 4.2.for循环和范围表达式
      • 4.2.1.for循环
      • 4.2.2.范围表达式
      • 4.2.3.用in检查成员关系
      • 4.2.4.函数可变参数
    • 4.3.中缀表达式
  • 5.字符串的定义和操作
    • 5.1.定义原生字符串
    • 5.2.字符串模板
    • 5.3.字符串判等
  • 6.总结
    • 6.1.类型推导
    • 6.2.变量声明
    • 6.3.函数声明
    • 6.4.高阶函数
    • 6.5.Lambda表达式
    • 6.6.表达式和流程控制
    • 6.7.字符串操作

0.前言

简单的知识点记录而已,参考书籍《Kotlin核心编程》

1.不一样的类型声明

声明变量,类型在变量的后面,如下:

val a: String = "Im a Kotlin"
// 等价写法,省略类型声明,也就是下面1.1中的类型推导
val a = "Im a Kotlin"

1.1.增强的类型推导

编译器可以在不显示声明类型的情况在,自动推导出需要的类型,如下:

val svalue = "Im a Kotlin"  // java.lang.String
val ivalue = 123   // int
val lvalue = 123L  // long
val fvalue = 123F  // float
val dvalue = 123.12  // double

上述可以在REPL中打印这些变量的类型,如 println(svalue.javaClass.name) 。

类型推导提高Kotlin这种静态类型语言的开发效率。

1.2.声明函数返回值类型

虽然Kotlin支持类型推导,但不代表可以不声明函数的返回值类型。

先看看如何用关键字 fun 定义一个函数, 如下:

fun sum(x: Int, y: Int): Int {return x + y
}

与声明变量一样,类型信息放在函数的后面,如果我们把返回的类型声明去掉就会报错,如下:

// 编译报错:Type mismatch: inferred type is Int but Unit was expected
fun sum(x: Int, y: Int) {return x + y
}

因为没有声明返回值的类型,函数默认为函数返回 Unit(相当于Java的 void ) 类型,但是方法体中实际 return 了 Int 类型,所以编译报错了。这种情况必须声明返回值类型

上面的函数还可以进一步增强写法,可以把函数体的 {} 去掉,用等号 = 定义函数,如下:

fun sum(x: Int, y: Int): Int = x + y
// 等价于,去掉了返回值类型
fun sum(x: Int, y: Int) = x + y

这种用单行表达式和等号的语法定义的函数,叫作表达式函数。普通的函数声明叫作代码块函数

在使用表达式函数定义的情况下,可以不声明函数的返回值类型。 但是如果递归,还是要声明返回值类型,如下:

// 没有返回值类型,会报错:Type checking has run into a recursive problem. Easiest workaround: specify types of your declarations explicitly
fun foo(n: Int) = if (n == 0) 1 else n * foo(n - 1)
// 正确写法,加上返回值类型的声明
fun foo(n: Int): Int = if (n == 0) 1 else n * foo(n - 1)

编译器不能针对递归函数的情况推导类型。

1.3.是否需要显示声明类型?

  • 如果是一个函数的参数:必须声明(参数类型)。
  • 一个非表达式函数:那就是代码块函数,除非函数没有返回(Unit),否则必须声明(返回值类型的)。
  • 递归函数:必须声明(返回值类型)。
  • 其他情况:建议声明,增强代码的可读性。

2.val和var的使用规则

Kotlin声明变量使用 varval 的概念。

  • var相当于Java中的变量,可变的。
  • val相当于Java中的 final 修饰的变量,不可变的(引用不可变)。

2.1.val的含义:引用不可变

val声明的变量的引用不可变,但是引用对象可以变,如下:

// val定义一个数组
val x = intArrayOf(1,2,3)
// 修改数组的引用,会报错,因为x是val修饰,引用不可变,不能指向另一个数组
x = intArrayOf(2,3,4)  
// 修改引用对象的某个值,可以,结果是[6,2,3]
x[0] = 6  

因此 val 声明的变量是只读变量,它的引用不可变,但是引用的对象(的可变成员)可以变。如下:

// 这里Book类的参数使用var声明
class Book(var name: String){fun printName(): Unit {println("这本书的名字是:$name")}  	 
}// 创建对象,打印name
val book: Book = Book("Kotlin核心编程")
book.printName()
// 修改Book的name的值,不报错,可以正常打印
book.name = "Java核心编程"
book.printName()

执行结果:

这本书的名字是:Kotlin核心编程
这本书的名字是:Java核心编程

如果把Book的参数 name 的参数类型改为 val,上述就会报错了。

注意,上述示例中类的使用后续会说明,这里只是说明 var 和 val 的区别。

开发中,建议优先使用 val、尽可能采用 val 、不可变对象以及纯函数(没有副作用的函数)来设计程序。

3.高阶函数和Lambda

函数式语言典型特征就是函数是头等公民,不仅可以像类一样在顶层直接定义一个函数,也可以在一个函数内部定义一个局部函数。

此外也可以直接将函数像普通变量一样传递给另一个函数,或者在其他函数内被返回。

3.1.抽象和高阶函数

抽象不多做解释,和Java的抽象一样理解。

高阶函数,可以理解为“把其他函数作为参数或者返回值的函数”,是一种高级的抽象机制,增强语言表达能力。

3.2.函数作为参数的需求

需求:有一堆人员Person的集合数据,有属性name、age和address,需要根据不同条件从集合中筛选人员,就可以把这里的“不同条件筛选”的行为抽象成一个参数,这个参数是一个类对象,也就是多个不同的筛选条件需求创建多个不同的类,这些类各自实现了一个筛选行为。

先定义一个data class声明Person数据类(后面会说明,其实就是相当于Java的POJO对象),如下:

// data class 声明一个Person数据类
data class Person(val name: String,val age: Int,val address: String
)

然后定义一个类,里面有一个方法,筛选符合条件的Person的方法,例如我们想要年龄age≥40岁的人员,如下:

class FilterPersonByAge {fun ageGe40Filter(person: Person): Boolean {return person.age >= 40}
}

现在,如何把上面类 FilterPersonByAge 的方法 ageGe40Filter 变成另一个函数的参数呢?也就是将 ageGe40Filter 变成一个函数类型的参数。

3.3.函数的类型

Kotlin中,函数类型的格式如下:

(Int) -> Unit

如上,Kotlin中的函数类型的声明遵循以下几点:

  • 通过 -> 符号来组织函数类型的参数类型和返回值类型,左边是参数类型,右边是返回值类型。
  • 必须用一个括号来包裹函数类型的参数类型。如果是一个没有参数的函数类型,参数类型部分用 () 表示,即 () -> Unit
  • 返回值类型即使是 Unit,也必须显示声明
  • 如果是多个参数的情况,使用逗号分割参数类型部分,即 (Int, String) -> Unit
  • Kotlin还支持为函数类型的参数类型部分指定名字,即 (errCode: Int, errMsg: String) -> Unit
  • 高阶函数支持返回另一个函数,即 (Int) -> ((Int) -> Unit),表示传入类型为Int的参数,返回另一个类型为 (Int) -> Unit 的函数,可以简化写法,即 (Int) -> Int -> Unit

所以,现在回到刚才Person筛选的问题,我们还缺少一个通过对Person数据进行筛选的过程方法,如下:

// 接收一个函数类型的参数test,函数的参数是Person类型,返回Boolean类型
fun filterPerson(persons: List<Person>, test: (Person) -> Boolean): List<Person> {val pList = mutableListOf<Person>()for (p in persons) {// 使用函数类型的参数进行筛选if (test(p)) {pList.add(p)}}return pList }

最后,如何将 FilterPersonByAge 类的方法 ageGe40Filter 作为一个参数传递给方法 filterPerson 呢?继续往下看。

3.4.方法和成员引用

Kotlin中,存在语法通过两个冒号来实现对某个类的方法进行引用,所以刚才的问题,我们需要将 FilterPersonByAge 类的方法 ageGe40Filter 作为一个参数传递给方法 filterPerson,就可以这么写,如下:

// 先创建类的对象实例
val filterPersonByAge= FilterPersonByAge()
// 引用方法
filterPersonByAge::ageGe40Filter

到这里,我们涉及到Person的筛选整个过程都完成了,上下文中的完整代码如下:

// 定义一个数据类Person
data class Person (val name: String,val age: Int,val address: String
)// 创建一个筛选条件的类,实现一个筛选行为
// 不同的筛选条件,定义多个这样的类,但实现的方法都必须是 (Person) -> Boolean 的函数类型
class FilterPersonByAge {fun ageGe40Filter(person: Person): Boolean {return person.age >= 40}
}// 筛选过程,根据不同的筛选条件test进行筛选
// 接受一个函数类型test的参数
fun filterPerson(persons: List<Person>, test: (Person) -> Boolean): List<Person> {	val pList = mutableListOf<Person>()for (p in persons) {// 使用函数类型的参数进行筛选if (test(p)) {pList.add(p)}}return pList 
}fun main(){val p1 = Person("张三", 30, "北京")val p2 = Person("李四", 40, "上海")val p3 = Person("王五", 50, "深圳")val p4 = Person("赵六", 60, "广州")val allPersonList: List<Person> = listOf(p1, p2, p3, p4)// 方法引用val filterPersonByAge = FilterPersonByAge()val filterPersonList: List<Person> = filterPerson(allPersonList, filterPersonByAge::ageGe40Filter)for (p in filterPersonList) {println("筛选人员:${p}")}}

执行代码,结果如下:

筛选人员:Person(name=李四, age=40, address=上海)
筛选人员:Person(name=王五, age=50, address=深圳)
筛选人员:Person(name=赵六, age=60, address=广州)

3.5.匿名函数

回顾上述Person筛选的过程,其中类 FilterPersonByAge 的定义不是很好的方案,因为每增加一个筛选条件就要定义一个类似的类,比较麻烦,所以可以使用匿名函数改进优化。

Kotlin支持在缺省函数名的情况在,直接定义一个函数,所以上述代码可以这样改造,取消类 FilterPersonByAge 以及方法 ageGe40Filter 定义,直接执行 filterPerson 方法的时候传递一个匿名函数,如下:

// 直接展示关键代码处改造
...
// 取消定义FilterPersonByAge以及方法引用, 直接传递匿名函数
val filterPersonList: List<Person> = filterPerson(allPersonList, fun(person: Person): Boolean {return person.age>=40
})
...

是不是更简洁了,其实还有更简单的方法进行优化,这就是Lambda表达式。

3.6.Lambda表达式

Lambda是更简单的概念,可以理解为简化表达后的匿名函数,实质上就是一种语法糖。

上面的 3.5.匿名函数 中,我们使用匿名函数进行了改进,这里分析一下这个匿名函数:

  • fun(person: Person)显得比较啰嗦,因为编译器会推导类型,所以只需要一个变量,如 person 就行了。
  • 匿名函数体中的关键字 return 也可以省略,这里返回的是一个有值的表达式。
  • 模仿函数类型的语法,可以用 -> 把参数和返回值连在一起。

如上,使用Lambda继续改进后就变成如下:

val filterPersonList: List<Person> = filterPerson(allPersonList, {person -> person.age>=40
})

这就是 Lambda 表达式,和匿名函数一样,是一种函数字面量,接着说明 Lambda 的具体语法。

先用 Lambda 表达式定义一个加法的操作:

// sum是定义的表达式,表达式的参数是两个Int类型,返回值是一个Int类型
val sum: (Int, Int) -> Int = {x: Int, y:Int -> x + y}

因为类型推导,这里可以简化如下:

// 声明了 Lambda 的参数类型,可以不声明 sum的函数类型
val sum = {x: Int, y:Int -> x + y}
// 等价于
// 声明了 sum的函数类型,可以不声明 Lambda 的参数部分类型
val sum: (Int, Int) -> Int = {x, y -> x + y}

如上,总结 Lambda 表达式的语法:

  • Lambda 表达式必须通过 {} 来包裹起来。
  • 如果 Lambda 声明了参数部分的类型,且返回值类型支持类型推导,那么 Lambda 变量就可以省略函数类型声明。
  • 如果 Lambda 声明了函数类型,那么 Lambda 的参数部分的类型可以省略。
  • 如果 Lambda 表达式的返回不是 Unit,那么默认最后一行表达式的值类型就是返回值类型。如:
val sum: (Int, Int) -> Int = {x: Int, y:Int -> val s = x + y// 返回值是ss
}

3.7.函数、Lambda

我们很容易对Kotlin的 fun 声明函数、Lambda 表达式的语法产生混淆,因为它们都可以存在花括号 {},现在我们来做个区分:

  • fun 在没有等号、只有花括号的情况在,就是我们最常见的代码块函数体,如果返回类型不是 Unit,必须带着 return。如下:
fun foo(x: Int) {println(x)
} 
fun foo(x: Int, y: Int): Int{return x + y
}
  • fun 带有等号,是单表达式函数体,该情况下可以省略 return。如下:
fun foo(x: Int, y: Int) = x + y
  • 不管是 val/var 还是 fun,如果同时有等号、花括号的语法,那构建的就是一个 Lambda 表达式,Lambda 的参数在花括号内部声明。所以,如果左侧是 fun,那么就是 Lambda 式函数体,也必须通过 () 或者 invoke 来调用 Lambda,如:
// fun、等号、花括号,定义 Lambda 表达式
fun foo(x: Int, y: Int) = {x + y}
// val、等号、花括号,定义 Lambda 表达式
val foo1 = {x: Int, y: Int -> x + y}// 调用Lambda表达式 foo
println(foo(1,2)())
println(foo(1,2).invoke())// 调用Lambda表达式 foo1
println(foo1(2,2))
println(foo1.invoke(2,2))

4.面向表达式编程

4.1.枚举类和when表达式

这里介绍定义枚举结构以及一个非常强大的表达式,也就是when表达式。

下面我们先定义枚举,然后使用枚举进行when的使用来说明用法。

4.1.1.枚举是类

在 Kotlin 中,枚举是通过一个枚举类enum来实现的,如下:

enum class Day{MON,TUE,WED,THU,FRI,SAT,SUN
}

和Java类似,不同的是枚举类的定义多了一个 class 关键字,表示这是一个枚举类。

但是 Kotlin 的枚举类不简单,由于是一个类,自然也拥有构造函数,以及定义额外的属性和方法,如下:

enum class Day(val day: Int){MON(1),TUE(2),WED(3),THU(4),FRI(5),SAT(6),// 注意这里结尾有一个分号,为了和下面的方法或者属性定义区分开SUN(7);fun getDayNumber(): Int {return day}
}

需要注意,枚举类中如果存在额外的方法或者属性定义,必须在枚举项的最后一个结尾处追加分号,这是必须的。

4.1.2.用when代替if-else

上看了解了如何定义一个枚举类,现在我们使用枚举模拟业务,例如,给一周的几天计划了不同的活动,安排如下:

  • 周六周日打蓝球
  • 周五约会
  • 平时里如果晴天就学习,否则就睡觉

设计代码如下,利用一个函数并结合刚才的枚举进行表示:

fun schedule(day: Day, sunny: Boolean){if (day == Day.SAT && day == Day.SUN) {// 打篮球} else if (day == Day.FRI) {// 约会} else {if (sunny) {// 学习} else {// 睡觉}}
}

看起来很麻烦,存在了不少的if-else分支,更好的改进方案就是使用when表达式,如下:

fun schedule(day: Day, sunny: Boolean) = when(day) {Day.SAT,Day.SUN -> "打篮球"Day.FRI -> "约会"else -> when {sunny -> "学习"else -> "睡觉"}
}

是不是简单了很多。

4.1.3.when表达式具体语法

具体语法如下:

  1. 一个完成的 when 表达式类似 switch 语句,由 when 关键字开始,用花括号包含多个逻辑分支,每个分支由 -> 连接,不在需要 switch 的 break 关键字了,由上到下匹配,一直匹配完为止,否则执行 else 分支的逻辑,类似 switch 的 default。
  2. 每个逻辑分支具有返回值,最终整个 when 表达式的返回类型就是所有分支相同的返回类型,或公共的父类型。
  3. when 关键字的参数可以省略,如上面的例子中,else 分支中的 when 表达式就没有参数,只不过该情况下,分支 -> 的左侧部分需要返回布尔值(when没有参数的时候),否则编译报错。
  4. 表达式可以组合,上面的例子中,when 表达式中的 else 分支中还有一个 when,也就是嵌套了一层,可以如下修改:
// when的参数省略,里面的每一行表达式按照3中的要求,左侧是布尔值
fun schedule2(day: Day, sunny: Boolean): String = when {day == Day.SAT || day == Day.SUN -> "打篮球"day == Day.FRI -> "约会"sunny -> "学习"else -> "睡觉"
}

when是不是很优雅,其实when的实际使用中威力不仅如此,这里不多说明,后续会说明。

4.2.for循环和范围表达式

这里说明 for 循环的语法和引用。

4.2.1.for循环

在Java中,一个 for 循环的基本写法如下:

for (int i=1; i<=10; i++) {//
}

Kotlin中,for 循环的表达式更简洁,如下:

for (i in 1..10) {println(i)
}
// 等价于
for (i: Int in 1..10) {println(i)
}

此外,for 循环中我们还可以通过调用一个 withIndex 方法,提供一个键值元组:

val a = intArrayOf(1,2,3)for ((index, value) in a.withIndex()) {println("下标:$index, 数值:$value")}

4.2.2.范围表达式

上述的 for 循环中的 1…10 这种语法,就是范围表达式(range),Range 表达式是通过 rangeTo 函数实现的,通过 “…” 操作符和某种类型的对象组成,除了整型的基本类型外,该类型需要实现 java.lang.Comparable 接口

另外,当对整数进行 for 循环时,Kotlin 还提供了一个 step 函数来定义迭代的步长,如下:

// 打印结果是 1,3,5,7,9
for (i in 1..10 step 2) {println(i)
}

如果是倒序呢?可以使用 downTo 方法实现:

// 打印结果是 10,8,6,4,2
for (i in 10 downTo 1 step 2 ) {println(i)
}

另外还有一个 until 函数实现一个半开区间,如下:

// 打印结果是 1,2,3,4,5,6,7,8,9 并不包含10
for (i in 1 until 10 ) {println(i)
}

4.2.3.用in检查成员关系

in 关键字,在 Kotlin 中还可以用来检查一个元素是否是另一个区间或集合中的成员,举例如下:

// 结果是 true
"a" in listOf<String>("b","c","d","a")

如果在 in 前面加上感叹号,那就是相反的判断结果:

// 结果是 false
"a" !in listOf<String>("b","c","d","a")

in 还可以结合范围表达式来表示更多的含义:

4.2.4.函数可变参数

Kotlin 通过关键字 vararg 来定义函数中的可变参数,类似 Java 中的 “…” 的效果。

需要注意,Java 中的可变参数必须位于方法的最后一个参数,Kotlin 可没有这个限制(不过调用的时候有点不同)。

同样的,Kotlin 的可变参数在函数体中也是以数组的方式来使用可变参数的,如下:

// 可变参数names位于函数第一个参数
fun printlnNames(vararg names: String, msg: String) {println(msg)for (name in names) {println(name)}
}
// 调用的时候,第二个参数通过指定参数名区分开可变参数
printlnNames("张三", "李四", "王五", msg = "下面有请运动员入场:")

如果可变参数位于函数最后一个,那就可以不指定参数名了,如下:

fun printlnNames( msg: String, vararg names: String) {println(msg)for (name in names) {println(name)}
}
printlnNames("下面有请运动员入场:","张三", "李四", "王五")

此外,还可以使用星号(*)来传入函数外部的变量作为可变参数的变量,如下:

fun printlnNames( msg: String, vararg names: String) {println(msg)for (name in names) {println(name)}
}val namearr = arrayOf("张三", "李四", "王五")printlnNames("下面有请运动员入场:", *namearr)

4.3.中缀表达式

上面我们已经见识到了不少 Kotlin 中的奇特用法,例如 in、step、downTo、until,它们可以不通过点号,而是通过中缀表达式来调用,从而更简洁直观,这些是如何实现的呢?

先看 Kotlin 标准库中另一个类似的方法 to 的设计,这是一个通过泛型实现的方法,可以返回一个 Pair,如下:

public infix fun <A, B> A.to(that: B): Pair<A, B>

在 Kotlin 中,to 这种形式定义的函数成为 中缀函数,一个中缀函数的表达形式非常简单,可以理解成这样:

A 中缀方法 B

可以发现,如果我们要定义一个中缀函数,需要满足如下条件:

  • 该中缀函数必须是某个类型的扩展函数或者成员方法。
  • 该中缀函数只能有一个参数。
  • 虽然 Kotlin 的函数参数支持默认值,但是中缀函数不能有默认值,否则以上形式的 B 会缺失,从而对中缀表达式的语义造成破坏。
  • 同样,该参数也不能是可变参数,因为需要保持参数始终为1个。

由于 to 会返回 Pair 这种键值对的结构数据,因此经常把它和 Map 结合在一起使用,如下:

mapOf(1 to "one", 2 to "two", 3 to "three")

现在再自定义一个中缀函数,如下:

class Person {// 使用 infix 修饰infix fun called(name: String){println("My name is $name")}
} 

因为方法 called 使用了 infix 修饰,所以这样调用:

val person = Person()
// 结果打印:My name is Kotlin
person called "Kotlin"

同样的,上面也可以使用普通的语法调用这个中缀函数,如下:

// 结果打印:My name is Java
person.called("Java")

上述的完整代码如下:

class Person {// infix修饰,中缀函数的定义infix fun called(name: String){println("My name is $name")}
}val person = Person()// 中缀函数调用
person called "Kotlin"
// 普通语法调用
person.called("Java")

5.字符串的定义和操作

现在开始说明 Kotlin 中的字符串基础语法。

和 Java 一样,也是通过双引号定义字符串,是不可变的对象。

val str = "hello world"

熟悉的操作如下:

// 11
println(str.length)
// hello
println(str.substring(0,5))
// hello world, !!!
println(str + ", !!!")
// hello Kotlin
println(str.replace("world", "Kotlin"))

String 是一个字符序列,所以可以进行遍历:

// 打印结果:HELLO WORLD
for (c in str.uppercase()) {print(c)
}

还可以访问字符序列的成员

// h
println(str[0])
// h
println(str.first())
// d
println(str.last())
// d
println(str[str.length - 1])

此外,Kotlin 的字符串还有很多丰富的 API,如下:

// true
println("".isEmpty())
// false
println(" ".isEmpty())
// true
println(" ".isBlank())
// ed
str.filter { c -> c in 'a'..'e' }.forEach { c -> print(c) }

更多的字符串的语法,请查阅 Kotlin API 文档

5.1.定义原生字符串

Kotlin 中,支持原生字符串,如下:

val str = """hello, my name is kotlin,\n today verry happy, because i am a kotlin programmer.\n hahaha ...bye ~~~
"""println(str)

打印结果如下:

      hello, my name is kotlin,\n today verry happy, because i am a kotlin programmer.\n hahaha ...bye ~~~

简而言之,用这种三个双引号定义的字符串,最终的打印格式和代码中呈现的格式一致,不会解释转化为转义字符,例如上面的 \n。

原生字符串如果来描述 HTML,会非常方便。

5.2.字符串模板

先看一个字符串常见的拼接:

fun printlnInfo(name: String, age: Int){println("my name is " + name + ", age is " + age)
}

一个简单的拼接,用了2个加号,如果多变量就更繁琐了,而 Kotlin 引入了字符串模板改进了这一点,支持将变量植入字符串,高进上述代码,如下:

fun printlnInfo(name: String, age: Int) {println("my name is ${name}, age is ${age}")
}

通过${name}这种格式将变量传入字符串,最终提高了代码的紧凑性和可读性。

除了上面将变量传入字符串,也可以将表达式传入,并且支持在表达式中使用双引号,如下:

val name = "Kotlin"
val hobby = "2"val message = "my name is ${name}, my hobby is ${ if(hobby == "1") "篮球" else "足球" }"println(message)

5.3.字符串判等

Kotlin 中的判等性主要有两种类型:

  • 结构相等:通过操作符 == 来判断两个对象的内容是否想等。
  • 引用相等:通过操作符 === 来判断两个对象的引用是否一样,与之相反的判断操作符是 !==。如果比较的实在运行时的原始类型,比如 Int,那么 === 判断的效果也等价于 ==。

具体例子检测字符串两种类型的判等性:

var a = "Java"
var b = "Java"
var c = "Kotlin"
var d = "Kot"
var e = "lin"
var f = d + e// true
println(a == b)
// true
println(a === b)
// true
println(c == f)
// false
println(c === f)

6.总结

6.1.类型推导

Kotlin 拥有比 Java 更强大的类型推导功能,避免了静态语言在编码时需要书写大量类型的弊端。

但这不是万能的,在使用代码块函数体时,必须显示声明返回值类型。

此外一些复杂的情况,例如递归,返回值类型也不能省略。

6.2.变量声明

Kotlin 中使用 var 和 val 声明变量以及一些类的成员属性,代表它们的引用可变性。

开发中优先推荐使用 val。

6.3.函数声明

Kotlin 中,普通的函数分为代码块体和表达式体,前者类似 Java 中定义函数的习惯,后者因为是一个表达式,可以说省略 return 关键字。

6.4.高阶函数

Kotlin 中函数是头等公民,所以程序中可以到处声明函数,也可以作为值传递以及作为另一个函数的返回值。

函数作为参数的时候,需要使用函数引用表达式进行传值。

柯里化是函数作为返回值的一种应用,但是很少使用。

6.5.Lambda表达式

可以当做另一种匿名函数。

6.6.表达式和流程控制

表达式更加安全,更利于组合,且更强的表达能力。

流程控制可以利用 if、when、try、range、中缀等表达式,写出更强大的代码。

for 循环更简洁。

6.7.字符串操作

字符串是不可变对象,有丰富的API。

支持原生字符串、字符串模板等功能。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/12717.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

项目实施奖励方式调查

此调查主要了解各企业的项目激励方式&#xff08;只有3个问题&#xff09;。 相应的调查结果也将定时公布&#xff0c;希望对企业项目管理、企业激励方面有一定帮助。 感谢大家的支持。 链接地址为&#xff1a;项目实施奖励方式调查

数据的可计算性

目的&#xff1a;更容易被计算机程序分析和处理。所以涉及到数据准备、清洗、整理、格式化等过程。 1、标准化数据格式&#xff1a;所有数据遵循统一的格式和标准。比如日期和时间以标准格式存储。 2、清洗数据&#xff1a;移除或修正 错误数据和异常值&#xff0c;包括去除重…

Flutter 依据JSON数据自动生成实体类

json自动化生成工具 点击这里可以跳转 页面是这样的 然后在左边输入你的json数据&#xff0c;它会自动生成对应的实体类 生成的实体类是如下&#xff1a; import package:json_annotation/json_annotation.dart; part merch_region.g.dart;JsonSerializable()class MerchReg…

国网645协议报文解析软件

本文分享一款支持国网DL645-2007规约的报文解析软件&#xff0c; 链接: https://pan.baidu.com/s/1ngbBG-yL8ucRWLDflqzEnQ 提取码: y1de 主界面如下图所示&#xff1a; 本解析软件同时内嵌规约文档&#xff0c;支持一键打开文档&#xff0c;功能如下&#xff1a; 同时支持模…

堆的数组实现

前言 本次博客来讲解一下堆的数组实现&#xff0c;好吧还是会结合图例&#xff0c;让大家理解 堆的定义 什么是堆&#xff1f; 堆是一颗完全二叉树。它的性质是父节点一定大于或者一定小于子节点 每一个结点都要满足这个性质就是堆 堆的特性是堆顶的数据一定是最大或最小…

ROS仿真多点导航

仿真环境启动&#xff1a; 1、启动并进入到相应环境&#xff1a; roscarroscar-virtual-machine:~/artcar_simulation$ 启动gazebo环境&#xff1a; roslaunch artcar_gazebo artcar_gazebo.launch 启动move_base&#xff1a; roslaunch artcar_nav artcar_move_base.launc…

3D数字化技术如何改变汽车行业?

近年来&#xff0c;新能源汽车行业加速发展&#xff0c;新车型密集发布&#xff0c;汽车保有量和车龄的增加&#xff0c;也同时点燃了汽车后市场的增长引擎。对于车企而言&#xff0c;如何全方面优化汽车从研发、生产、售后到营销的各个环节&#xff0c;以便适应快速变化的市场…

中文域名和英文域名有什么区别?中文域名有哪些优势?

作为企业网站的重要标识&#xff0c;域名在数字化时代扮演着重要角色。而随着中国互联网的快速发展&#xff0c;中文域名也逐渐崭露头角&#xff0c;受到国内企业和用户的关注。本文国科云作为中国科学院控股有限公司旗下的域名管理品牌&#xff0c;简单为大家介绍下中文域名和…

使用make_blobs生成数据并使用KNN机器学习算法进行分类和预测以及可视化

生成数据 使用make_blobs生成数据并使用matplotlib进行可视化 完整代码&#xff1a; from sklearn.datasets import make_blobs # KNN 分类器 from sklearn.neighbors import KNeighborsClassifier # 画图工具 import matplotlib.pyplot as plt # 数据集拆分工具 from sklea…

【代码随想录】链表

2024.5.11-2024.5.15 移除链表元素 #判断头节点是否空&#xff0c;#并且头节点为要删除的值&#xff0c;头节点向后移while head and head.valval:headhead.nextif not head: returncurhead#当前指针指向的下一个元素为val,当前指针指向下一个的下一个#否则&#xff0c;当前指…

Win7远程桌面连接不上:原因及专业解决方案

Win7远程桌面连接作为一种方便的工具&#xff0c;使得用户可以从一台计算机远程访问和操作另一台计算机。然而&#xff0c;有时用户可能会遇到Win7远程桌面连接不上的情况&#xff0c;这可能是由于多种原因导致的。 一、原因分析 1. 网络设置问题&#xff1a;确保计算机与远程…

Java 语言的特点分析及应用

Java语言自问世以来&#xff0c;因其独特的设计理念和广泛的应用领域&#xff0c;成为了编程语言中的一颗璀璨明星。以下是对Java语言特点的详细分析及其实际应用场景&#xff0c;希望能帮助面试者更好地理解和掌握Java的优势。 1. 简单易学 Java的语法简单&#xff0c;类似于…

可用在vue自动导入的插件unplugin-auto-import

在大多数vue3开发中&#xff0c;基本所有页面都会引用vue3 componsition api&#xff0c;如下代码 想这种vue3 架构中自带的api&#xff0c;如果在全局配置一下的话&#xff0c;就可以减少一部分代码量&#xff0c;只是在代码编译的时候&#xff0c;会添加相应的引用&#xff…

【Stable Diffusion】 训练方法篇

一、四种模型训练方法简介 Stable Diffusion 有四种训练模型的方法&#xff1a;Textual Inversion、Hypernetwork、LoRA 和 Dreambooth 。它们的训练方法存在一定差异&#xff0c;我们可以通过下面对比来评估使用哪种训练方式最适合你的项目。 如果你知道模型中已经可以产生你…

企业架构系统之-IT系统建设如何做好技术选型

背景 近日有幸与行业同仁交流工作心得&#xff0c;在讨论中&#xff0c;他们提到一个平时工作当中我们都会遇到和经历的一个问题&#xff1a;作为架构师&#xff0c;在日常工作中应如何进行技术选型&#xff1f;面对众多框架和组件中&#xff0c;我们又应如何选择&#xff0c;…

Postgresql源码(128)深入分析JIT中的函数内联llvm_inline

相关 《Postgresql源码&#xff08;127&#xff09;投影ExecProject的表达式执行分析》 《LLVM的ThinLTO编译优化技术在Postgresql中的应用》 《LLVM&#xff08;5&#xff09;ORC实例分析》 1 JIT优化效果 create table t1(i int primary key, j int, k int); insert into t1…

Google IO 2024有哪些看点呢?

有了 24 小时前 OpenAI 用 GPT-4o 带来的炸场之后&#xff0c;今年的 Google I/O 还未开始&#xff0c;似乎就被架在了一个相当尴尬的地位&#xff0c;即使每个人都知道 Google 将发布足够多的新 AI 内容&#xff0c;但有了 GPT-4o 的珠玉在前&#xff0c;即使是 Google 也不得…

秋招算法——AcWing101——拦截导弹

文章目录 题目描述思路分析实现源码分析总结 题目描述 思路分析 目前是有一个笨办法&#xff0c;就是创建链表记录每一个最长下降子序列所对应的节点的链接&#xff0c;然后逐个记录所有结点的访问情况&#xff0c;直接所有节点都被访问过。这个方法不是很好&#xff0c;因为需…

一件代发海外仓系统解决方案:提升效率,海外仓管理更轻松

现在跨境电商的发展真的太快了&#xff0c;很多商家也开始转战跨境&#xff0c;希望能打开国际市场。作为跨境电商非常重要的一环&#xff0c;海外仓的需求自然也越来越大。 随着海外仓市场的火爆&#xff0c;越来越多的海外仓企业都意识到&#xff0c;提升海外仓运营的效率变得…

互联网摸鱼日报(2024-05-15)

互联网摸鱼日报(2024-05-15) 36氪新闻 宇树发布 Unitree G1人形机器人&#xff0c;9.9万元起 | 最前线 腾讯游戏恢复流水增长&#xff0c;金融科技与企业服务收入占比持续超三成 好利来推的茶饮副牌“好茶”&#xff0c;有点霸王茶姬的感觉 OpenAI全部的秘密&#xff0c;藏…