Kotlin学习笔记之基础知识

ops/2025/3/13 9:21:23/

本内容是建立在有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?)或泛型。 后者情况下会把数字装箱

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

vala:Int=10000

print(a===a)// 输出“true”

valboxedA:Int?=a

valanotherBoxedA:Int?=a

print(boxedA===anotherBoxedA)// !!!输出“false”!!!

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

vala:Int=10000

print(a==a)// 输出“true”

valboxedA:Int?=a

valanotherBoxedA: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

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

vall=1L+3// Long + Int => Long

运算

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

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

val x = (1 shl 2) and 0x000FF000

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

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

浮点数比较

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

  • 区间实例以及区间检测:a..b、 x in a..b、 x !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(cinstr){

println(c)

}

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

valtext="""

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

字符串模板

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

vali=10vals="i = $i"// 求值结果为 "i = 10"

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

vals="abc"

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

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

valprice="""

${'$'}9.99

"""

包与导入

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

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

控制流:if、when、for、while

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

例如:

传统if:

varmax:Int

if(a>b){

max=a

}else{

max=b

}

可以写成:

valmax=if(a>b)aelseb

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

valmax=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){

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

invalidNumbers->print("x is valid")

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

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

}

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

funhasPrefix(x:Any)=when(x){

isString->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(itemincollection)print(item)

或者代码块

for(item:Intinints){

//……

}

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

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

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

for(iinarray.indices){

print(array[i])

}

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

或者你可以用库函数 withIndex

for((index,value)inarray.withIndex()){

println("the element at $index is $value")

}

While 循环

while 和 do..while 照常使用

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

Break 与 Continue 标签

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

loop@for(iin1..100){

// ……

}

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

loop@for(iin1..100){

for(jin1..100){

if(……)break@loop

}

}

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

标签处返回

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

funfoo(){

ints.forEach{

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

print(it)

}

}

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

funfoo(){

ints.forEachlit@{

if(it==0)

return@lit

print(it)

}

}

或者简写成funfoo(){

ints.forEach{

if(it==0)return@forEach

print(it)

}

}

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

funfoo(){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)”。


http://www.ppmy.cn/ops/165381.html

相关文章

力扣hot100_二叉树

二叉树的建立与遍历 #include <iostream> #include <vector> #include <queue> using namespace std;// 定义二叉树节点 struct TreeNode {int val;TreeNode* left;TreeNode* right;TreeNode(int x) : val(x), left(nullptr), right(nullptr) {} };// 函数&…

Git 的基本概念和使用方式(附有思维导图)

一、Git 简介 Git 是一个开源的分布式版本控制系统&#xff0c;由 Linus Torvalds 在 2005 年为帮助管理 Linux 内核开发版本而开发 。与集中式版本控制系统&#xff08;如 SVN&#xff09;不同&#xff0c;在分布式系统中&#xff0c;每个开发者的本地机器都拥有一个完整的 G…

理解 XSS 和 CSP:保护你的 Web 应用免受恶意脚本攻击

在当今的互联网世界中&#xff0c;Web 应用的安全性至关重要。随着网络攻击技术的不断演进&#xff0c;开发者需要采取多种措施来保护用户数据和应用的完整性。本文将深入探讨两种关键的安全概念&#xff1a;XSS&#xff08;跨站脚本攻击&#xff09; 和 CSP&#xff08;内容安…

unet模型在车道线检测上的应用【代码+数据集+python环境+GUI系统】

unet模型在车道线检测上的应用【代码数据集python环境GUI系统】 VIL100数据集介绍 VIL-100 数据集是一个新的视频实例车道线检测数据集&#xff0c;由张玉君、朱磊等人在 2021 年发表的 ICCV 论文中提出。以下是对该数据集的详细介绍&#xff1a; 数据规模&#xff1a;包含 1…

Windows 上安装配置 Apache Tomcat 及Tomcat 与 JDK 版本对应

Apache Tomcat 是一种广泛使用的 Web 服务器和 Java 容器&#xff0c;对于部署和运行 Java Web 应用程序至关重要。它的可靠性和强大的功能使其成为全球开发人员和组织的首选。 在这篇文章中&#xff0c;我们将介绍在 Windows 机器上安装 Apache Tomcat 的过程&#xff0c;以确…

Python----数据可视化(Pyecharts一:介绍安装,全局配置,系列配置)

一、PyEcharts介绍 1.1、概况 Echarts 是一个由百度开源的数据可视化&#xff0c;凭借着良好的交互性&#xff0c;精巧的图表设计&#xff0c;得到了众多开发者的认可。而 Python 是一门富有表达力的语言&#xff0c;很适合用于数据处理。当数据分析遇上数据可视化时&#xff…

数学建模:MATLAB强化学习

一、强化学习简述 强化学习是一种通过与环境交互&#xff0c;学习状态到行为的映射关系&#xff0c;以获得最大积累期望回报的方法。包含环境&#xff0c;动作和奖励三部分&#xff0c;本质是智能体通过与环境的交互&#xff0c;使得其作出的动作所得到的决策得到的总的奖励达…

数字隔离器,如何提升储能系统的安全与效能?

随着全球对光伏、风电等可再生能源需求的持续增长&#xff0c;在全球能源转型的浪潮中&#xff0c;储能技术凭借着可平衡能源供需、提高能源利用效率等优势&#xff0c;已成为实现 “双碳” 目标的核心支撑。据国家能源局公布数据显示&#xff0c;截至2024年底&#xff0c;我国…