Kotlin学习笔记之基础知识

本内容是建立在有java的基础上去学习Kotlin的这门语言的,所以更多的是记录一些与java不同的之处,或者是Kotlin的特性等。

基本类型

在 Kotlin 中,所有东西都是对象,在这个意义上讲我们可以在任何变量上调用成员函数和属性。 一些类型可以有特殊的内部表示------例如,数字、字符和布尔值可以在运行时表示为原生类型值,但是对于用户来说,它们看起来就像普通的类。 在本节中,我们会描述 Kotlin 中使用的基本类型:数字、字符、布尔值、数组与字符串。

数字

Kotlin 处理数字在某种程度上接近 Java,但是并不完全相同。例如,对于数字没有隐式拓宽转换(如 Java 中 int可以隐式转换为long------译者注),另外有些情况的字面值略有不同。

字面常量

数值常量字面值有以下几种:

  • 十进制: 123
    • Long 类型用大写 L 标记: 123L
  • 十六进制: 0x0F
  • 二进制: 0b00001011

Kotlin 同样支持浮点数的常规表示方法:

  • 默认 double:123.5123.5e10
  • Float 用 f 或者 F 标记: 123.5f

注意: 在 Kotlin 中字符不是数字,且不支持八进制,且字符不是数值

表示方式

在 Java 平台数字是物理存储为 JVM 的原生类型,除非我们需要一个可空的引用(如 Int?)或泛型。 后者情况下会把数字装箱

注意数字装箱不必保留同一性:

val a:Int=10000

print(a===a)// 输出"true"

val boxedA:Int?=a

val anotherBoxedA:Int?=a

print(boxedA===anotherBoxedA)// !!!输出"false"!!!

另一方面,它保留了相等性:

val a:Int=10000

print(a==a)// 输出"true"

val boxedA:Int?=a

val anotherBoxedA:Int?=a

print(boxedA==anotherBoxedA)// 输出"true"

类型转换

Kotlin 不能像java那样类型那样强制转换,int类型数据不能默认和long类型的数值进行比较大小。

比如在java中:

int a;

long b = 100;

a = b;(在kotlin中这样是不允许的,必须写成:a = b.toInt())

每个数字类型支持如下的转换:

  • toByte(): Byte
  • toShort(): Short
  • toInt(): Int
  • toLong(): Long
  • toFloat(): Float
  • toDouble(): Double
  • toChar(): Char

但是在做计算的过程中,隐式转换又存在,比如:

val l=1L+3// Long + Int => Long

运算

Kotlin支持数字运算的标准集,运算被定义为相应的类成员(但编译器会将函数调用优化为相应的指令)。 参见运算符重载

对于位运算,没有特殊字符来表示,而只可用中缀方式调用命名函数,例如:

复制代码
val x = (1 shl 2) and 0x000FF000

这是完整的位运算列表(只用于 IntLong):

  • shl(bits) -- 有符号左移 (Java 的 <<)
  • shr(bits) -- 有符号右移 (Java 的 >>)
  • ushr(bits) -- 无符号右移 (Java 的 >>>)
  • and(bits) -- 位与
  • or(bits) -- 位或
  • xor(bits) -- 位异或
  • inv() -- 位非

浮点数比较

相等和大小的 比较与java相同,Kotlin增加了一种区间实例与区间检测,如下:

  • 区间实例以及区间检测:a..bx in a..bx !in a..b

字符

之前有提到过,字符不是数字

字符用 Char 类型表示。它们不能直接当作数字

fun check(c: Char) {

if (c == 1) { // 错误:类型不兼容

// ......

}

}

字符字面值用单引号括起来: '1'。 特殊字符可以用反斜杠转义。 支持这几个转义序列:\t\b\n\r\'\"\\\$。 编码其他字符要用 Unicode 转义序列语法:'\uFF00'

数组

数组在 Kotlin 中使用 Array 类来表示,它定义了 get 和 set 函数(按照运算符重载约定这会转变为 [])和 size 属性,以及一些其他有用的成员函数:
class Array<T> private constructor() {
val size: Int
operator fun get(index: Int): T
operator fun set(index: Int, value: T): Unit
operator fun iterator(): Iterator<T>
// ......
}

我们可以使用库函数 arrayOf() 来创建一个数组并传递元素值给它,这样 arrayOf(1, 2, 3) 创建了 array [1, 2, 3]。 或者,库函数 arrayOfNulls() 可以用于创建一个指定大小的、所有元素都为空的数组。
另一个选项是用接受数组大小和一个函数参数的工厂函数,用作参数的函数能够返回给定索引的每个元素初始值:
// 创建一个 Array<String> 初始化为 ["0", "1", "4", "9", "16"]
val asc = Array(5, { i -> (i * i).toString() })
如上所述,[] 运算符代表调用成员函数 get() 和 set()。
注意: 与 Java 不同的是,Kotlin 中数组是不型变的(invariant)。这意味着 Kotlin 不让我们把 Array<String> 赋值给 Array<Any>,以防止可能的运行时失败(但是你可以使用 Array<out Any>, 参见类型投影)。
Kotlin 也有无装箱开销的专门的类来表示原生类型数组: ByteArray、 ShortArray、IntArray 等等。这些类和 Array 并没有继承关系,但是它们有同样的方法属性集。它们也都有相应的工厂方法:
val x: IntArray = intArrayOf(1, 2, 3)
x[0] = x[1] + x[2]

字符串

字符串除了具有java的特性意外,字符串的元素------字符可以使用索引运算符访问: s[i] 。 可以用 for 循环迭代字符串:

for (cin str){

println(c)

}

原生字符串 使用三个引号(""")分界符括起来,内部没有转义并且可以包含换行和任何其他字符:

val text="""

for (c in "foo")

print(c)

"""

你可以通过 trimMargin() 函数去除前导空格:
val text = """
|Tell me and I forget.
|Teach me and I remember.
|Involve me and I learn.
|(Benjamin Franklin)
""".trimMargin()
默认 | 用作边界前缀,但你可以选择其他字符并作为参数传入,比如 trimMargin(">")。

**备注:**前导空格,我的理解就是去除"|"前面的空格

("ABC\n123\n456"===("""ABC

|123

|456""".trimMargin())) // 结果为false

("ABC\n123\n456"==("""ABC

|123

|456""".trimMargin())) // 结果为true

字符串模板

字符串可以包含模板表达式 ,即一些小段代码,会求值并把结果合并到字符串中。 模板表达式以美元符($)开头,由一个简单的名字构成:

val i=10val s="i = $i"// 求值结果为 "i = 10"

或者用花括号括起来的任意表达式:

val s="abc"

val str="s.length is {s.length}"// 求值结果为 "abc.length is 3"

原生字符串和转义字符串内部都支持模板。 如果你需要在原生字符串中表示字面值 $ 字符(它不支持反斜杠转义),你可以用下列语法:

val price="""

{''}9.99

"""

包与导入

如果出现名字冲突,可以使用 as 关键字在本地重命名冲突项来消歧义:
import foo.Bar// Bar 可访问
import bar.Baras bBar// bBar 代表"bar.Bar"

与 Java 不同的是Kotlin 没有单独的"import static"语法; 所有这些声明都用 import 关键字导入。

控制流:if、when、for、while

在 Kotlin 中,if是一个表达式,即它会返回一个值。 因此就不需要三元运算符(条件 ? 然后 : 否则),因为普通的 if 就能胜任这个角色。

例如:

传统if:

var max:Int

if (a>b){

max=a

}else{

max=b

}

可以写成:

val max=if (a>b)aelseb

if的分支可以是代码块,最后的表达式作为该块的值:

val max=if (a>b){

print("Choose a")

a

}else {

print("Choose b")

b

}

备注: 如果你使用 if 作为表达式而不是语句(例如:返回它的值或者把它赋给变量),该表达式需要有 else 分支。

When 表达式

when 取代了类 C 语言的 switch 操作符。其最简单的形式如下:

when (x ) {
1->print ( "x == 1" )
2->print ( "x == 2" )
else -> { // 注意这个块
print ( "x is neither 1 nor 2" )
}
}

when 将它的参数和所有的分支条件顺序比较,直到某个分支满足条件。 when 既可以被当做表达式使用也可以被当做语句使用。如果它被当做表达式, 符合条件的分支的值就是整个表达式的值,如果当做语句使用, 则忽略个别分支的值。(像 if 一样,每一个分支可以是一个代码块,它的值是块中最后的表达式的值。)

如果其他分支都不满足条件将会求值 else 分支。 如果 when 作为一个表达式使用,则必须有 else 分支, 除非编译器能够检测出所有的可能情况 都已经覆盖了。

如果很多分支需要用相同的方式处理,则可以把多个分支条件放在一起,用逗号分隔:

when (x){

0,1->print("x == 0 or x == 1")

else ->print("otherwise")

}

我们可以用任意表达式(而不只是常量)作为分支条件:

when (x){

parseInt(s)->print("s encodes x")

else ->print("s does not encode x")

}

我们也可以检测一个值在(in)或者不在(!in)一个区间或者集合中:

when (x){

in 1..10->print("x is in the range")

in validNumbers->print("x is valid")

!in 10..20->print("x is outside the range")

else ->print("none of the above")

}

另一种可能性是检测一个值是(is)或者不是(!is)一个特定类型的值。注意: 由于智能转换,你可以访问该类型的方法和属性而无需任何额外的检测。

fun hasPrefix(x:Any)=when (x){

is String->x.startsWith("prefix")

else ->false

}

when 也可以用来取代 if-else if链。 如果不提供参数,所有的分支条件都是简单的布尔表达式,而当一个分支的条件为真时则执行该分支:

when {

x.isOdd()->print("x is odd")

x.isEven()->print("x is even")

else ->print("x is funny")

}

For 循环

for 循环可以对任何提供迭代器(iterator)的对象进行遍历,这相当于像 C# 这样的语言中的 foreach 循环。语法如下:

for (itemin collection)print(item)

或者代码块

for (item:Intin ints){

//......

}

for 可以循环遍历任何提供了迭代器的对象。即:

有一个成员函数或者扩展函数 iterator(),它的返回类型有一个成员函数或者扩展函数 next(),并且有一个成员函数或者扩展函数 hasNext() 返回 Boolean

如果你想要通过索引遍历一个数组或者一个 list,你可以这么做:

for (iin array.indices){

print(array[i])

}

**注意:**这种"在区间上遍历"会编译成优化的实现而不会创建额外对象。

或者你可以用库函数 withIndex

for ((index,value)in array.withIndex()){

println("the element at index is value")

}

While 循环

while 和 do..while 照常使用

备注: 在循环中 Kotlin 支持传统的 break 和 continue 操作符

Break 与 Continue 标签

在 Kotlin 中任何表达式都可以用标签(label)来标记。 标签的格式为标识符后跟 @ 符号,例如:abc@fooBar@都是有效的标签。 要为一个表达式加标签,我们只要在其前加标签即可。

loop@for (iin 1..100){

// ......

}

现在,我们可以用标签限制 break 或者continue:

loop@for (iin 1..100){

for (jin 1..100){

if (......)break@loop

}

}

标签限制的 break 跳转到刚好位于该标签指定的循环后面的执行点。 continue 继续标签指定的循环的下一次迭代。

标签处返回

Kotlin 有函数字面量、局部函数和对象表达式。因此 Kotlin 的函数可以被嵌套。 标签限制的 return 允许我们从外层函数返回。 最重要的一个用途就是从 lambda 表达式中返回。

fun foo(){

ints.forEach{

if (it==0)return // nonlocal return from inside lambda directly to the caller of foo()

print(it)

}

}

这个 return 表达式从最直接包围它的函数即 foo 中返回。 (注意,这种非局部的返回只支持传给内联函数的 lambda 表达式。) 如果我们需要从 lambda 表达式中返回,我们必须给它加标签并用以限制 return。

fun foo(){

ints.forEachlit@{

if (it==0)

return@lit

print(it)

}

}

或者简写成fun foo(){

ints.forEach{

if (it==0)return@forEach

print(it)

}

}

或者,我们用一个匿名函数替代 lambda 表达式。 匿名函数内部的 return 语句将从该匿名函数自身返回

fun foo(){ints.forEach(fun (value:Int){

if (value==0)return // local return to the caller of the anonymous fun,

i.e. the forEach loop

print(value)

})

}

当要返一个回值的时候,解析器优先选用标签限制的 return,即

return @a1

意为"从标签 @a 返回 1",而不是"返回一个标签标注的表达式 (@a 1)"。

相关推荐
Kapaseker1 天前
实战 Compose 中的 IntrinsicSize
android·kotlin
A0微声z3 天前
Kotlin Multiplatform (KMP) 中使用 Protobuf
kotlin
alexhilton4 天前
使用FunctionGemma进行设备端函数调用
android·kotlin·android jetpack
lhDream4 天前
Kotlin 开发者必看!JetBrains 开源 LLM 框架 Koog 快速上手指南(含示例)
kotlin
RdoZam4 天前
Android-封装基类Activity\Fragment,从0到1记录
android·kotlin
Kapaseker4 天前
研究表明,开发者对Kotlin集合的了解不到 20%
android·kotlin
西岸行者5 天前
学习笔记:SKILLS 能帮助更好的vibe coding
笔记·学习
starlaky5 天前
Django入门笔记
笔记·django
勇气要爆发5 天前
吴恩达《LangChain LLM 应用开发精读笔记》1-Introduction_介绍
笔记·langchain·吴恩达
悠哉悠哉愿意5 天前
【单片机学习笔记】串口、超声波、NE555的同时使用
笔记·单片机·学习