【Kotlin】基础变量、集合和安全操作符

文章目录

    • 数字
    • 字面常量
    • 显式转换
      • 数值类型转换背后
    • 位运算符
    • 字符串
    • 字符串模板
    • 修饰符
    • 数组
    • 集合(Kotlin自带)
    • 通过序列提高效率
      • 惰性求值
      • 序列的操作方式
        • 中间操作
        • 末端操作
    • 可null类型
      • 安全调用操作符 ?.
      • 操作符 ?:
      • 非空断言操作符 !!
    • 使用类型检测及自动类型转换
      • 安全的类型转换 as?
      • 返回和跳转

在Kotlin中,所有东西都是对象,在这个意义上讲我们可以在任何变量上调用成员函数和属性。
一些java中的基本数据类型可以有特殊的内部表示——例如,数字(int)、字符和布尔值可以在运行时表示为原生类型值

数字

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

Kotlin提供了如下的内置类型来表示:

Byte(字节):1字节
Short(短整型):2字节
Int(整型):4字节
Long(长整型):8字节
Float(单精度浮点型):4字节
Double(双精度浮点型):8字节
Boolean(布尔型):1字节
Char(字符型):2字节

注意:在Kotlin中字符不是数字,字符用Char类型表示。它们不能直接当作数字(可以用char.code表示出对应编码)

  val char = 'c'print(char.code)//99

字面常量

可以使用下划线使数字常量更易读,并且不影响赋值

val int = 1_0000_0000
print(int / 10000)
//10000

还可以:

val LongNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

显式转换

由于不同的表示方式,较小类型并不是较大类型的子类型。这代表他们不能进行直接的隐式转换:

在这里插入图片描述

我们可以显式转换来拓宽数字

fun main(){val int = 1_0000_0000val long: Long = int.toLong()
}

数值类型转换背后

var x = 5 // 这行代码创建了一个Int类型的变量x以及一个Int类型值为5的对象。x保存了该对象的引用
var z : Long = x.toLong() // 这行代码创建了一个新的Long变量z。x对象的toLong()函数被调用并且创建了一个值为5的Long对象,该Long对象的引用被存储在z中

如果该数值超出了新对象所能存储的范围该怎么办?
就像一大杯水装进一个小杯,有些会溢出

如果Long的值超出了Int能容纳的范围,那么编译器将会舍弃超出的部分,会得到一个奇怪(仍可计算)的数值。例如:

var x = 1234567890123
var y: Int = x.toInt()
println(y) // 1912276171

(C语言初学者应该很熟悉的问题)

位运算符

(只用于Int和Long。Kotlin的特性:可以重写操作符函数的逻辑):

shl(bits) – 有符号左移 (Java 的 <<)
shr(bits) – 有符号右移 (Java 的 >>)
ushr(bits) – 无符号右移 (Java 的 >>>)
and(bits) – 位与
or(bits) – 位或
xor(bits) – 位异或
inv() – 位非
区间实例以及区间检测:a..b、 x in a..b、 x !in a..b

字符串

字符串的元素——字符(Char)可以使用索引运算符访问:str[i]。可以用for循环迭代字符串:

for (c in str) {println(c)
}

当你使用双引号(“)表示字符串时,它只能包含单行文本。
而当你使用三个双引号(”“”)表示字符串时,它可以包含多行文本
转义采用传统的反斜杠方式。
原生字符串 使用三个引号"""分界符括起来,内部没有转义并且可以包含换行和任何其他字符:

val text = """for (c in "foo")print(c)
"""

字符串模板

字符串可以包含模板表达式,即一些小段代码,会求值并把结果合并到字符串中。模板表达式以美元符$开头再加上变量的名字。

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

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

  • 结构相等。通过操作符==来判断两个对象的内容是否相等。
  • 引用相等。引用相等由双等号以及其否定形式!= = =操作判断。a = = = b当且仅当a和b指向同一个对象时求值为true。如果比较的是运行时的原始类型,比如Int,那么’= = =判断的效果也等价于’=='。
var a = "hello"
var b = "hello"
var c = "Kotlin"
var d = "Kot"
var e = "lin"
var f = d + e
a == b // true
a === b // true
c == f // true
c === f // false

结构相等由= =以及其否定形式!= =操作判断。
那么a = = b这样的表达式会被翻译为 a?.equals(b) ?: (b === null) 也就是说如果a不是null则调用equals(Any?)函数,否则即a是null检查b是否与null引用相等。
(精妙!)
好多等号一直显示高亮

修饰符

注意,在kotlin中默认的修饰符是public

  • private 所以如果我们给一个类声明为private, 我们就不能在定义这个类之外的文件中使用它。 另一方面,如果我们在一个类里面使用了private修饰符,那访问权限就被限制在这个类里面了。甚至是继承这个类的子类也不能使用它。
  • protected 在Java中是包、类及子类可访问,而在Kotlin中只允许类及子类。
  • internal 它与Java的default有点像但也有所区别。如果是一个定义为internal的包成员的话,对所在的整个module可见。如果它是一个其它领域的成员,它就需要依赖那个领域的可见性了。 比如如果写了一个private类,那么它的internal修饰的函数的可见性就会限制与它所在的这个类的可见性。
  • public 这是最没有限制的修饰符。这是默认的修饰符,成员在任何地方被修饰为public,很明显它只限制于它的领域。

数组

和java不同的是,新建一个数组用类Array实现,并且还有一个size属性及get和set,万物皆对象,由于kotlin使用[]重载了get和set方法,所以我们可以通过下标很方便的获取或者 设置数组对应位置的值。 Kotlin标准库提供了arrayOf()创建数组和xxArrayOf创建特定类型数组

val myArray = arrayOf(1, 2, 3)

还有:

val nums = arrayOf(1, 2, 3)
val numbers = intArrayOf(10, 20, 30)
//intArrayOf 新建int类型数组
val array1 = Array(10, { k -> k * k })
//可用表达式
val emptyArray = emptyArray<String>()
//括号内可以填参数,表示新建数组的大小

和Java不一样的,Kotlin的数组是容器类,提供了ByteArray,CharArray,ShortArray,IntArray,LongArray,BooleanArray,FloatArrayDoubleArray
那么以下两个有什么区别呢?

val arrayA = arrayOf(1, 2, 3)
val arrayB = intArrayOf(1, 2, 3)

当我们显式指明数据类型后:

val arrayA: Array<Int> = arrayOf(1, 2, 3)
val arrayB: IntArray = intArrayOf(1, 2, 3)

区别注意一下,arrayOf() 是Kotlin自带的,很便携,可以进行自判断类型生成对应的Array并填写泛型类。
但是Array是会使用装箱技术。它使用对象引用来存储每个整数(或其他基本数据类型),因此它的内存占用比较大。而IntArray由于不需要装箱和拆箱的过程,IntArray拥有更好的性能和更高的效率。但是也有一定的缺点,他比Array的api要少…

集合(Kotlin自带)

Kotlin有三个主要的集合类型(List、Set和Map),也可以分为可变集合和不可变集合//反正用得不多
简单的List、Set和Map是不可变的,这意味着集合被初始化后不能再添加或移除元素。如果想要添加或移除元素,Kotlin提供了可变的子类型作为替代方案:MutableList、MutableSet和MutableMap。因此,如果想要利用List的所有优势,并希望能够更新其内容,请使用MutableList。
Kotlin的List<out T>类型是一个提供只读操作如size、get等的接口。和Java类似,它继承自Collection<T>进而继承自Iterable<T>。 改变list的方法是由MutableList<T>加入的,且适用于SetMap

可变集合,顾名思义,就是可以改变的集合。可变集合都会有一个修饰前缀“Mutable”,比如MutableList。这里的改变是指改变集合中的元素,比如以下可变集合:

val list = mutableListOf(1, 2, 3, 4, 5)
list[0] = 0 // 变成[0, 2, 3, 4, 5]

Kotlin没有专门的语法结构创建list或set。要用标准库的方法如listOf()、mutableListOf()、setOf()、mutableSetOf()。 创建map可以用mapOf(a to b, c to d)。
最主要可以用 map[key] = value 我很喜欢这样用

var list = listOf(“a”, “b”, “c”)
for(c in list) {
println©
}

fun main() {var map = TreeMap<String, String>()map["0"] = "0 haha"map["1"] = "1 haha"map["2"] = "2 haha"println(map["1"])
}
val numbers: MutableList<Int> = mutableListOf(1, 2, 3)
val readOnlyView: List<Int> = numbers
println(numbers)        // "[1, 2, 3]"
numbers.add(4)
println(readOnlyView)   // "[1, 2, 3, 4]"
readOnlyView.clear()    // -> 不能编译
val strings = hashSetOf("a", "b", "c", "c")

Kotlin中提供了很多操作结合的函数,例如:

val newList = list.map{it * 2} // 对集合遍历,在遍历过程中,给每个元素都乘以2,得到一个新的集合
val mStudents = students.filter{it.sex == "m"} // 筛选出性别为男的学生
val scoreTotal = students.sumBy{it.score} // 拥挤和中的sumby实现求和

通过序列提高效率

因为filter方法和map方法都会返回一个新的集合,也就是说会产生两个临时集合(跟当初java:String一样),因为list会先调用filter方法,然后产生的集合会再次调用map方法。如果list中的元素非常多,这将会是一笔不小的开销。为了解决这一问题,序列(Sequence)就出现了。(Builder?)

list.asSequence().filter {it > 2}.map {it * 2}.toList()
  • 首先通过asSequence方法将一个列表转换为序列,然后在这个序列上进行相应的操作,最后通过toList方法将序列转为列表。将list转换为序列,在很大程度上就提高了上面操作集合的效率。

因为在使用序列的时候filter方法和map方法的操作都没有创建额外的集合,这样当集合中的元素数量巨大的时候,就减少了大部分开销。

  • 在Kotlin中,序列中元素的求值是惰性的,这就意味着在利用序列进行链式求值的时候,不需要像操作普通集合那样,每进行一次求值操作,就产生一个新的集合保存中间数据。

惰性求值

在编程语言理论中,惰性求值表示一种在需要时才进行求值的计算方式。 在使用惰性求值的时候,表达式不在它被绑定到变量之后就立即求值,而是在该值被取用时才去求值。 通过这种方式,不仅能得到性能上的提升,还有一个重要的好处就是它可以构造出一个无限的数据类型。

序列的操作方式

list.asSequence().filter {it > 2}.map {it * 2}.toList()

序列总共执行了两类操作分别是:

  • filter{it > 2}.map{it * 2}:filter和map的操作返回的都是序列,我们将这类操作称为中间操作。
  • toList():这一类操作将序列转换为List,我们将这类操作称为末端操作。

Kotlin中序列的操作就分为:

中间操作
  • 中间操作都是采用惰性求值的,例如:
list.asSequence().filter {println("filter($it)")
}.map {println("map($it)")
}

上面操作中的println方法没有执行,这说明filter和map方法的执行被延迟了,正所谓惰性求值。惰性求值仅仅在该值被需要的时候才会真正去求值。 那么怎么去触发这个”被需要“的状态?即——末端操作

末端操作
  • 末端操作就是一个返回结果的操作,它的返回值不能是序列,必须是一个明确的结果, 比如列表、数字、对象等表意明确的结果。
  • 末端操作一般都放在链式操作的末尾, 在执行末端操作的时候,会去触发中间操作的延迟计算,也就是将”被需要“这个状态被激活了
list.asSequence().filter {println("filter($it)")it > 2
}.map {println("map($it)")it * 2
}.toList()
// 输出↓
filter(1)
filter(2)
filter(3)
map(3)
filter(4)
map(4)
filter(5)
map(5)
[6, 8, 10]

可以看到,所有的中间操作都被执行了。从上面执行打印的结果我们发现,它的执行顺序与我们预想的不一样。

  • 普通集合在进行链式操作的时候会先在list上调用filter,然后产生一个结果列表,接下来map就在这个结果列表上进行操作。
  • 而序列则不一样,序列在执行链式操作的时候,会将所有的操作都应用在一个元素,也就是说,第一个元素执行完所有的操作之后,第二个元素再去执行所有的操作,以此类推。

可null类型

因为在Kotlin中一切都是对象,一切都是可null的。这就会防止出现NPE异常。当某个变量的值可以为null的时候,必须在声明处的类型后添加?来标识该引用可为空。 Kotlin通过?将是否允许为空分割开来,甚至Any也分可空和不可空。

var value1: String
value1 = null        // 编译错误 Null can not be a value of a non-null type String
var value2 : String?
value2 = null       // 编译通过

在对可空变量进行操作时,如果变量是可能为空的,那么将不能直接调用,否则将会报错。

安全调用操作符 ?.

当你对一个可空类型的变量进行操作时,如果直接调用方法或访问属性,可能会导致空指针异常,那么此时可以使用 ?.

val length = name?.length

如果name不是null,length将返回name的长度;如果name是null,length将直接返回null,而不会抛出异常。

操作符 ?:

该操作符允许我们为可能为null的表达式提供一个默认值。

val length = name?.length ?: 0

在这个例子中,如果name不为null,length将返回其长度;如果name为null,length将返回0。

非空断言操作符 !!

当你确定一个变量绝对不为空时,你可以使用!!操作符,这将告诉编译器说,我已经排除了null值的情况。但是,如果该变量为null,还是会抛出空指针异常。

val length = name!!.length

请谨慎使用!!操作符,因为它会忽略Kotlin中对null安全性的所有保护措施。

使用类型检测及自动类型转换

is运算符检测一个表达式是否某类型的一个实例,如同Java的instanceof。 如果一个不可变的局部变量或属性已经判断出为某类型,那么检测后的分支中可以直接当作该类型使用,在大多数情况下,is操作符会进行智能转换。表示编译器将变量当作与其声明的类型不同的类型,而智能转换是说编译器替你自动地进行转换。 无需显式转换:

fun getStringLength(obj: Any): Int? {if (obj !is String) return null// obj 在这一分支自动转换为 String,这是因为Kotlin的编译器帮我们做了转换// 这称为Kotlin中的智能转换(Smart Casts)。官方文档中这样介绍: 当且仅当Kotlin的编译器// 确定在类型检查后该变量不会再改变,才会产生Smart Casts。return obj.length
}

只要编译器能够保证在介于判断对象类型和被使用之间不能修改变量,is操作符就会进行智能转换。

例如,在上面的代码中,编译器知道在介于调用is操作符和调用String的某个方法之间,item变量不能被赋予另一类型的引用。但是在一些特殊情况下,智能转换不会生效。例如,is操作符不会对类中的var属性进行智能转换,那是因为编译器无法保证该属性是否还会被更改。

安全的类型转换 as?

安全类型转换操作符as?尝试进行类型转换,如果失败了不会抛出异常,而是返回null。

val a: Int? = "123".toIntOrNull()//将字符串"123"转换为整数类型,由于转换成功,所以intValue的值为123。
val b: Int? = "abc" as? Int //将字符串"abc"尝试转换为整数类型,由于转换失败,所以intValueOrNull的值为null。
//a = 123  b = null

返回和跳转

Kotlin有三种结构化跳转表达式,和java差不太多:

  • return:默认从最直接包围它的函数或者匿名函数返回。
  • break:终止最直接包围它的循环。
  • continue:继续下一次最直接包围它的循环。
    在Kotlin中任何表达式都可以用标签label来标记。标签的格式为标识符后跟@符号,例如:test@
    要为一个表达式加标签,我们只要在其前加标签即可。
loop@ for (i in 1..100) {for (j in 1..100) {if (i + j == 100) break@loop//退出指定的循环  C语言的goto?}
}

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

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

相关文章

第二百一十七回 修改页面导航中遇到的问题

文章目录 1. 问题介绍2. 使用方法3. 代码与分析3.1 示例代码3.2 代码分析4. 内容总结我们在上一章回中介绍了"分享一种更新页面数据的方法"相关的内容,本章回中将介绍修改页面导航中遇到的问题.闲话休提,让我们一起Talk Flutter吧。 1. 问题介绍 我们在页面之间导…

学习——html基础

什么是HTML Hyper Text Markup Language (超文本标记语言) 标记又俗称标签(tag)&#xff0c;一般格式&#xff1a; 如 <h1></h1>标签里还可以有属性(Attribute)&#xff1a; <tagName Atrribute “value" /> 如 <meta charset"utf-8"…

selenium自动化webdriver下载及安装

1、确认浏览器的版本 在浏览器的地址栏&#xff0c;输入chrome://version/&#xff0c;回车后即可查看到对应版本 2、找到对应的chromedriver版本 2.1 114及之前的版本可以通过点击下载chromedriver,根据版本号&#xff08;只看大版本&#xff09;下载对应文件 2.2 116版本通过…

【进阶篇】YOLOv8实现K折交叉验证——解决数据集样本稀少和类别不平衡的难题,让你的模型评估更加稳健

YOLOv8专栏导航&#xff1a;点击此处跳转 K折交叉验证 K折交叉验证&#xff08;K-Fold Cross-Validation&#xff09;是一种常用的机器学习模型评估方法&#xff0c;可以帮助我们评估模型的性能&#xff0c;特别适用于数据集相对较小的情况。 在K折交叉验证中&#xff0c;将原…

redis相关面试题

1、说一说你在项目中的redis的应用场景&#xff1f; 需要频繁查询的数据&#xff0c;分布式锁&#xff0c;spring session 5大value类型&#xff1a;string hash list set zset基本上就是缓存为的是服务无状态&#xff0c;延申思考&#xff0c;看你的项目有哪些数据结构或对象…

final的详解

在Java中&#xff0c;final 关键字用于表示不可改变的实体&#xff0c;可以应用于变量、方法、类和指令重排序。它有不同的作用&#xff0c;具体取决于它被应用的上下文。 1.对于变量&#xff1a; 如果一个变量被声明为 final&#xff0c;则该变量的值在一旦被赋予后就不能再被…

Starting the Docker Engine...一直转圈

出现的问题&#xff1a; 原因排查&#xff1a; 看了网上的很多篇文章&#xff0c;每个原因都排查了&#xff0c;没有发现问题。 遇到这样的情况应先看自己是否安装成功 打开运行&#xff0c;在空框中输入powershell并点击确定&#xff1a; docker version 显示版本证明安装…

微信小程序-选择和分割打开地图选择位置的信息

一、 前言 废话不多说&#xff0c;单刀直入。 本文要实现的功能是微信小程序中打开地图选择位置&#xff0c;以及将返回的位置信息分割。 例如返回的位置信息是&#xff1a;广东省深圳市龙岗区xxxxx小区 分割后变成&#xff1a; {province: "广东省",city: "深…

042.Python异常处理_异常捕获

我 的 个 人 主 页&#xff1a;&#x1f449;&#x1f449; 失心疯的个人主页 &#x1f448;&#x1f448; 入 门 教 程 推 荐 &#xff1a;&#x1f449;&#x1f449; Python零基础入门教程合集 &#x1f448;&#x1f448; 虚 拟 环 境 搭 建 &#xff1a;&#x1f449;&…

SpringBoot前后端分离开发项目部署时,项目打包准备工作

第一步&#xff1a;项目打包之前&#xff0c;拉前后端代码 拉完代码后&#xff0c;再执行下面操作&#xff08;确保项目能正常启动并运行&#xff09; 后端&#xff08;执行如下操作&#xff09; mvn clean install -T 8 -Dmaven.test.skiptrue -Dmaven.compile.forktrue 执行…

JDK17 SpringBoot3 整合常见依赖

JDK版本&#xff1a;17 SpringBoot 整合Mybatis Plus 、Redis等 依赖文件 <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance&q…

【python】程序运行添加命令行参数argparse模块用法详解

Python标准库之argparse&#xff0c;详解如何创建一个ArgumentParser对象及使用 一. argparse介绍二. 使用步骤及参数介绍三. 具体使用3.1 设置必需参数3.2 传一个参数3.3 传多个参数3.4 位置参数和可选参数3.5 参数设置默认值3.6 其它用法 一. argparse介绍 很多时候&#xff…

Amazon CodeWhisperer 在 vscode 的应用

文章作者:旧花阴 CodeWhisperer 是一款可以帮助程序员更快、更安全地编写代码的工具&#xff0c;可以在他们的开发环境中实时提供代码建议和推荐。亚马逊云科技发布的这款代码生成工具 CodeWhisperer 最大的优势就是对于个人用户免费。以在 vscode 为例&#xff0c;演示安装过程…

LeetCode 1901. 寻找峰值 II:二分查找

【LetMeFly】1901.寻找峰值 II&#xff1a;二分查找 力扣题目链接&#xff1a;https://leetcode.cn/problems/find-a-peak-element-ii/ 一个 2D 网格中的 峰值 是指那些 严格大于 其相邻格子(上、下、左、右)的元素。 给你一个 从 0 开始编号 的 m x n 矩阵 mat &#xff0c…

【漏洞复现】CVE-2023-6895 IP网络对讲广播系统远程命令执行

漏洞描述 杭州海康威视数字技术有限公司IP网络对讲广播系统。 海康威视对讲广播系统3.0.3_20201113_RELEASE(HIK)存在漏洞。它已被宣布为关键。该漏洞影响文件/php/ping.php 的未知代码。使用输入 netstat -ano 操作参数 jsondata[ip] 会导致 os 命令注入。 开发语言:PHP 开…

原子学习笔记3——使用tslib库

一、tslib介绍 tslib 是专门为触摸屏设备所开发的 Linux 应用层函数库&#xff0c;并且是开源。 tslib 为触摸屏驱动和应用层之间的适配层&#xff0c;它把应用程序中读取触摸屏 struct input_event 类型数据&#xff08;这是输入设备上报给应用层的原始数据&#xff09;并进行…

2023-2024-2Java面向对象程序设计-阶段性测试2

填空题&#xff08;总分&#xff1a;10.00&#xff09; 1、Java程序中使用【 import 】关键字导入外部的包。 2、使用【 final 】关键字声明的类不能有子类。 4、JVM是【 Java Virtual Machine 】的英文简写。 5、面向对象编程思想的三个特性是【封装】、【继承】、【多态】。 …

数据分析师的职业规划与参考资料

数据分析师如何规划 参考&#xff1a;超详细的数据分析职业规划 一个产品的出现可以从业务和技术两个方向分析&#xff0c;业务需求技术支持产品的出现。 如果把职业也当成一个产品&#xff0c;也有类似的分析&#xff0c; 其中业务也就是领域&#xff0c;即这个业务领域的特点…

Power BI案例-医院数据集的仪表盘制作

数据集描述 医生数据集doctor 医生编号是唯一的&#xff0c;名称会存在重复 医疗项目数据projects 病例编号是唯一的&#xff0c;注意这个日期编号不是真正的日期。 日期数据date 这里的日期编号对应医疗项目数据中的日期编号 科室数据集Department 维度表 采购成本事实表…

知乎上高频提问:Redis到底是单线程还是多线程程序?

1.概述 这里我们先给出问题的全面回答&#xff1a;Redis到底是多线程还是单线程程序要看是针对哪个功能而言&#xff0c;对于核心业务功能部分(命令操作处理数据)&#xff0c;Redis是单线程的&#xff0c;主要是指 Redis 的网络 IO 和键值对读写是由一个线程来完成的&#xff…