【Kotlin】上手学习之类型篇

server/2025/1/19 8:07:25/

一、类型

1.1 基本类型

主要分为

  • 数字及其无符号版
  • 布尔
  • 字符
  • 字符串
  • 数组

1.1.1 数字

整数类型

Kotlin 提供了一组表示数字的内置类型。 对于整数,有四种不同大小的类型,因此值的范围也不同:

类型大小(比特数)最小值最大值
Byte8-128127
Short16-3276832767
Int32-2,147,483,648 (-2 ^ 31)2,147,483,647 (2 ^ 31 - 1)
Long64-9,223,372,036,854,775,808 (-2 ^ 63)9,223,372,036,854,775,807 (2^ 63 - 1)

当初始化一个没有显式指定类型的变量时,编译器会自动推断为自 Int 起足以表示该值的最小类型。 如果不超过 Int 的表示范围,那么类型是 Int。 如果超过了,那么类型是 Long。 如需显式指定 Long 值,请给该值追加后缀 L。 显式指定类型会触发编译器检测该值是否超出指定类型的表示范围。

kotlin">val one = 1 // Int
val threeBillion = 3000000000 // Long
val oneLong = 1L // Long
val oneByte: Byte = 1

这里与Java不同的是,Java里整型有int和Integer两种写法,前者表示的是基本的数据类型,后者表示的是int对象类型, 但是在kotlin里只有Int这一种。

浮点类型

这两个类型的大小不同,并为两种不同精度的浮点数提供存储:

类型大小(比特数)有效数字比特数指数比特数十进制位数
Float322486-7
Double64531115-16

与一些其他语言不同,Kotlin 中的数字没有隐式拓宽转换

例如,具有 Double 参数的函数只能对 Double 值调用,而不能对 FloatInt 或者其他数字值调用:

在这里插入图片描述

运行之后会报错提示:

kotlin">/Users/dxm/IdeaProjects/KotlinProject/src/main/kotlin/test/Test.kt:16:22
Kotlin: Argument type mismatch: actual type is 'kotlin.Int', but 'kotlin.Double' was expected./Users/dxm/IdeaProjects/KotlinProject/src/main/kotlin/test/Test.kt:18:22
Kotlin: Argument type mismatch: actual type is 'kotlin.Float', but 'kotlin.Double' was expected.

比如Java运行类似上面那串代码的话,就可以正常运行

kotlin">public class Hello {public static void main(String[] args) {Hello hello = new Hello();int a = 1;float f = 1.0f;double d = 1.0d;hello.printDouble(a);hello.printDouble(f);hello.printDouble(d);}public void printDouble(double num) {System.out.println(num);}
}

运行之后可以正常输出1.0

在这里插入图片描述

JVM 平台的数字表示

在 JVM 平台数字存储为原生类型 intdouble 等。 例外情况是当创建可空数字引用如 Int? 或者使用泛型时。 在这些场景中,数字会装箱为 Java 类 IntegerDouble 等。

对相同数字的可为空引用可能会引用不同的对象:

kotlin">fun main() {val a: Int = 100val boxedA: Int? = aval anotherBoxedA: Int? = aval b: Int = 10000val boxedB: Int? = bval anotherBoxedB: Int? = bprintln(boxedA === anotherBoxedA) // trueprintln(boxedB === anotherBoxedB) // false
}

第一个相等,是因为值是在[-128,127]之内,所以取的是Integer里缓存中的,指向的是缓存中同一块内存地址,所以会是同一个对象。

第二个不相等,是因为值是在[-128,127]之外,所以取的不是Integer里缓存中的,而是各自申请的一块内存地址,所以不会是同一个对象。

类似的可以参照下面Java代码运行的结果

Integer a = 127;
Integer b = 127;
System.out.println(a == b); // trueInteger e = 128;
Integer f = 128;
System.out.println(e == f); // falseInteger c = new Integer(128);
Integer d = new Integer(128);
System.out.println(c == d); // false
数据的比较

比较两个Int的变量相等,包含两个方面

  • == 是比较两个值是否相等,类似于Java的equals
  • === 是比较两个变量对象是否相等,类似于Java的==
显式数字转换

由于不同的表示方式,较小类型并不是较大类型的子类型。 如果它们是的话,就会出现下述问题:

kotlin">// 假想的代码,实际上并不能编译:
val a: Int? = 1 // 一个装箱的 Int (java.lang.Integer)
val b: Long? = a // 隐式转换产生一个装箱的 Long (java.lang.Long)
print(b == a) // 惊!这将输出“false”鉴于 Long 的 equals() 会检测另一个是否也为 Long

因此较小的类型不能隐式转换为较大的类型。 这意味着把Byte型值赋给一个Int变量必须显式转换:

kotlin">val b: Byte = 1 // OK, 字面值会静态检测
// val i: Int = b // 错误
val i1: Int = b.toInt()

所有数字类型都支持转换为其他类型:

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

很多情况都不需要显式类型转换,因为类型会从上下文推断出来, 而算术运算会有重载做适当转换,例如:

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

1.1.2 布尔

Boolean 类型表示可以有 truefalse 两个值的布尔对象。

布尔值的内置运算有:

  • ||——(逻辑
  • &&——(逻辑
  • !——(逻辑
kotlin">fun main() {val myTrue: Boolean = trueval myFalse: Boolean = falseval boolNull: Boolean? = nullprintln(myTrue || myFalse)// trueprintln(myTrue && myFalse)// falseprintln(!myTrue)// falseprintln(boolNull)// null
}

1.1.3 字符

如果字符变量的值是数字,那么可以使用 digitToInt() 函数将其显式转换为 Int 数字。

kotlin">fun main() {val a : Char = '1'val aDigitToInt = a.digitToInt()println(aDigitToInt) // 1
}

1.1.4 字符串

字符串双引号中的字符序列

kotlin">val str = "abcd 123"

字符串的元素——字符可以使用索引运算符访问:s[i]。 可以使用for循环遍历这些字符:

fun main() {val str = "abcd"for (c in str) {println(c)}
}

字符串是不可变的。 一旦初始化了一个字符串,就不能改变它的值或者给它赋新值。

字符串模板

模板表达式以美元符($)开头,要么由一个变量名构成:

kotlin">fun main() {val i = 10println("i = $i") // i = 10val letters = listOf("a","b","c","d","e")println("Letters: $letters") // Letters: [a, b, c, d, e]
}

要么是用花括号括起来的表达式:

kotlin">fun main() {val s = "abc"println("$s.length is ${s.length}") // abc.length is 3
}

1.1.5 数组

数组是一种保存固定数量相同类型或其子类型的值的数据结构。 Kotlin 中最常见的数组类型是对象类型数组,由Array类表示。

如果在对象类型数组中使用原生类型,那么会对性能产生影响,因为原生值都装箱成了对象。 为了避免装箱开销,请改用原生类型数组。

创建数组

创建数组一般有两种方式:

  • 函数,例如 arrayOf()、arrayOfNulls()) 或 emptyArray()。
  • Array 类的构造函数
函数创建数组
  1. 此示例使用 arrayOf() 函数并将项目值传递给它:
kotlin">fun main() {// Creates an array with values [1, 2, 3]val simpleArray = arrayOf(1, 2, 3)println(simpleArray.joinToString())// 1, 2, 3
}

joinToString 是 Kotlin 中一个非常有用的扩展函数,通常用于将集合或数组中的元素连接成一个字符串。它可以接收多个参数来定制输出字符串的格式。这个函数的作用是将集合中的每个元素按照给定的分隔符连接成一个字符串,可以指定前缀、后缀、分隔符以及如何转换元素。

  1. 此示例使用 arrayOfNulls()) 函数创建一个给定大小的数组,其中填充 null 元素:
kotlin">fun main() {// Creates an array with values [null, null, null]val nullArray: Array<Int?> = arrayOfNulls(3)println(nullArray.joinToString())// null, null, null
}
  1. 此示例使用emptyArray()函数创建一个空数组:
kotlin">    var exampleArray = emptyArray<String>()
Array构造函数创建数组

Array 构造函数采用数组大小和一个函数,该函数根据给定索引返回数组元素的值:

kotlin">fun main() {// Creates an Array<Int> that initializes with zeros [0, 0, 0]val initArray = Array<Int>(3) { 0 }println(initArray.joinToString())// 0, 0, 0// 创建一个 Array<String> 初始化为 ["0", "1", "4", "9", "16"]val asc = Array(5) { i -> (i * i).toString() }asc.forEach { print(it) }// 014916
}
嵌套数组

数组可以相互嵌套以创建多维数组:

kotlin">fun main() {// Creates a two-dimensional arrayval twoDArray = Array(2) { Array<Int>(2) { 0 } }println(twoDArray.contentDeepToString())// [[0, 0], [0, 0]]// Creates a three-dimensional arrayval threeDArray = Array(3) { Array(3) { Array<Int>(3) { 0 } } }println(threeDArray.contentDeepToString())// [[[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]], [[0, 0, 0], [0, 0, 0], [0, 0, 0]]]
}
数组求和
kotlin">fun main() {val sumArray = arrayOf(1, 2, 3)// Sums array elementsprintln(sumArray.sum())// 6
}
将数组转换为集合
转换为 List 或 Set

要将数组转换为 List 或 Set,请使用 .toList() 和 .toSet() 函数。

kotlin">fun main() {val simpleArray = arrayOf("a", "b", "c", "c")// Converts to a Setprintln(simpleArray.toSet())// [a, b, c]// Converts to a Listprintln(simpleArray.toList())// [a, b, c, c]}
转换为 Map
kotlin">fun main() {val pairArray = arrayOf("apple" to 120, "banana" to 150, "cherry" to 90, "apple" to 140)// Converts to a Map// The keys are fruits and the values are their number of calories// Note how keys must be unique, so the latest value of "apple"// overwrites the firstprintln(pairArray.toMap())// {apple=140, banana=150, cherry=90}}
原生类型数组

如果将 Array 类与原始值一起使用,这些值将被装箱到对象中。作为替代方案,您可以使用基元类型数组,它允许您将基元存储在数组中,而不会产生装箱开销的副作用:

Primitive-type arrayEquivalent in Java
BooleanArrayboolean[]
ByteArraybyte[]
CharArraychar[]
DoubleArraydouble[]
FloatArrayfloat[]
IntArrayint[]
LongArraylong[]
ShortArrayshort[]

这些类与 Array 类没有继承关系,但它们具有相同的函数和属性集。

下面示例创建 IntArray 类的实例:

kotlin">fun main() {// Creates an array of Int of size 5 with the values initialized to zeroval exampleArray = IntArray(5)println(exampleArray.joinToString())// 0, 0, 0, 0, 0
}
  • 要将原始类型数组转换为对象类型数组,请使用 .toTypedArray() 函数。
  • 要将对象类型数组转换为原始类型数组,请使用 .toBooleanArray()、.toByteArray()、.toCharArray() 等。

1.1.6 无符号整型

除了整数类型,对于无符号整数,Kotlin 还提供了以下类型:

TypeSize (bits)Min valueMax value
UByte80255
UShort16065,535
UInt3204,294,967,295 (2 ^ 32 - 1)
ULong64018,446,744,073,709,551,615 (2 ^ 64 - 1)

1.2 类型检测与类型转换

在 Kotlin 中,可以执行类型检查以在运行时检查对象的类型。类型转换将对象转换为不同的类型。

1.2.1 is 与 !is 操作符

使用 is 操作符或其否定形式 !is 在运行时检测对象是否符合给定类型:

kotlin">if (obj is String) {print(obj.length)
}if (obj !is String) { // 与 !(obj is String) 相同print("Not a String")
} else {print(obj.length)
}

这个类似于Java的instanceof关键字

1.2.2 智能转换

大多数场景都不需要在 Kotlin 中使用显式转换操作符,因为编译器跟踪不可变值的is-检测以及显式转换,并在必要时自动插入(安全的)转换:

kotlin">fun demo(x: Any) {if (x is String) {print(x.length) // x 自动转换为字符串}
}

编译器足够聪明,能够知道如果反向检测导致返回那么该转换是安全的:

kotlin">if (x !is String) returnprint(x.length) // x 自动转换为字符串

或者转换在 &&|| 的右侧,而相应的(正常或否定)检测在左侧:

kotlin">// `||` 右侧的 x 自动转换为 String
if (x !is String || x.length == 0) return// `&&` 右侧的 x 自动转换为 String
if (x is String && x.length > 0) {print(x.length) // x 自动转换为 String
}

智能转换用于when表达式和while循环也一样:

kotlin">when (x) {is Int -> print(x + 1)is String -> print(x.length + 1)is IntArray -> print(x.sum())
}

智能转换适用于以下情形:

val 局部变量总是可以,局部委托属性除外。
val 属性如果属性是 privateinternal,或者该检测在声明属性的同一模块中执行。 智能转换不能用于 open 的属性或者具有自定义 getter 的属性。
var 局部变量如果变量在检测及其使用之间未修改、没有在会修改它的 lambda 中捕获、并且不是局部委托属性。
var 属性决不可能,因为该变量可以随时被其他代码修改。

1.2.3 “不安全的”转换操作符

通常,如果转换是不可能的,转换操作符会抛出一个异常。因此,称为不安全的。 Kotlin 中的不安全转换使用中缀操作符 as

kotlin">val x: String = y as String

请注意,null不能转换为String因该类型不是可空的。 如果y为空,上面的代码会抛出一个异常。 为了让这样的代码用于可空值,请在类型转换的右侧使用可空类型:

kotlin">val x: String? = y as String?

1.2.4 “安全的”(可空)转换操作符

为了避免异常,可以使用安全转换操作符as?,它可以在失败时返回null

kotlin">val x: String? = y as? String

请注意,尽管事实上as?的右边是一个非空类型的String,但是其转换的结果是可空的。

比如下面这段代码

kotlin">fun main() {val x = IntArray(5)val str: String = x as Stringprintln("str is $str")
}

运行之后会抛出异常

kotlin">Exception in thread "main" java.lang.ClassCastException: class [I cannot be cast to class java.lang.String ([I and java.lang.String are in module java.base of loader 'bootstrap')

如果换成可空的话

kotlin">fun main() {val x = IntArray(5)val str: String? = x as? Stringprintln("str is $str")
}

至少是可以正常执行的,运行结果是

kotlin">str is null

所以我们平时如果写代码的时候,如果为了防止异常的话,在类型转换的时候尽量用这种可空的。如果业务中必须要转换过来的类型的话,那就不用可空的。


http://www.ppmy.cn/server/159573.html

相关文章

题解 CodeForces 430B Balls Game 栈 C/C++

题目传送门&#xff1a; Problem - B - Codeforceshttps://mirror.codeforces.com/contest/430/problem/B翻译&#xff1a; Iahub正在为国际信息学奥林匹克竞赛&#xff08;IOI&#xff09;做准备。有什么比玩一个类似祖玛的游戏更好的训练方法呢&#xff1f; 一排中有n个球…

微服务容器化部署好处多吗?

微服务容器化部署好处有很多&#xff0c;包括环境一致性、资源高效利用、快速部署与启动、隔离性与安全性、版本控制与回滚以及持续集成与持续部署。这些优势助力应用可靠稳定运行&#xff0c;提升开发运维效率&#xff0c;是现代软件架构的优质选择。UU云小编认为微服务容器化…

MySQL查询相关内容

创建员工库和表&#xff1b; mysql> create database mydb8_worker; Query OK, 1 row affected (0.01 sec)mysql> use mydb8_worker; Database changed mysql> create table t_worker(-> department_id int(11) not null comment 部门号,-> worker_id int(11)…

网络安全面试题及经验分享

本文内容是i春秋论坛面向专业爱好者征集的关于2023年面试题目和答案解析&#xff0c;题目是真实的面试经历分享&#xff0c;具有很高的参考价值。 shiro反序列化漏洞的原理 Shiro反序列化漏洞的原理是攻击者通过精心构造恶意序列化数据&#xff0c;使得在反序列化过程中能够执…

【2024年华为OD机试】 (B卷,200分)- 区间交集(Java JS PythonC/C++)

一、问题描述 题目解析 问题描述 给定一组闭区间&#xff0c;其中部分区间存在交集。要求&#xff1a; 求出任意两个区间的交集&#xff08;称为公共区间&#xff09;。如果公共区间之间存在交集&#xff0c;则需要合并这些公共区间。最终按升序排列输出合并后的区间列表。…

SparkSQL函数

文章目录 1. SparkSQL函数概述2. SparkSQL内置函数2.1 常用内置函数分类2.2 常用数组函数2.2.1 array()函数1. 定义2. 语法3. 示例 2.3 常用日期与时间戳函数2.4 常见聚合函数2.5 常见窗口函数 3. SparkSQL自定义函数3.1 自定义函数分类3.2 自定义函数案例演示 1. SparkSQL函数…

在nvidia jetson nx 板子上使用VPI+cuda backend计算光流

nvida的VPI可以调用CPU、GPU、PVA、VIC、NVENC、OFA等后端资源。因此恰当的使用vpi可以把部分需要gpu的计算让其他的计算资源来承担来降低cpu的负载&#xff0c;降低cpu过载造成设备卡死、计算异常等各种各样的风险。 下面介绍一下使用VPIcuda backend 计算LK稀疏光流。nvidia官…

Spark Streaming的核心功能及其示例PySpark代码

Spark Streaming是Apache Spark中用于实时流数据处理的模块。以下是一些常见功能的实用PySpark代码示例&#xff1a; 基础流处理&#xff1a;从TCP套接字读取数据并统计单词数量 from pyspark import SparkContext from pyspark.streaming import StreamingContext# 创建Spar…