Kotlin 2.1.0 入门教程(四)

news/2025/1/22 4:26:12/

基本类型

从某种意义上说,一切都是对象,因为您可以在任何变量上调用成员函数和属性。

虽然某些类型在运行时具有优化的内部表示形式(如数字、字符、布尔值等),但它们看起来和行为都像普通类。

即使基本类型(如 IntCharBoolean 等)在运行时被优化为原始值,但它们在代码中仍然表现为对象,可以调用成员函数和属性。

kotlin">fun main() {val number = 42number.toDouble() // 调用 Int 的成员函数。val isTrue = trueisTrue.toString() // 调用 Boolean 的成员函数。
}

整型

对于整数,有四种类型,它们具有不同的大小,因此值范围也不同。

整数类型包括 ByteShortIntLong,它们分别具有不同的存储大小和值范围。

kotlin">fun main() {val byte: Byte = 127 // 8 位,范围:-128 到 127。val short: Short = 32767 // 16 位,范围:-32768 到 32767。val int: Int = 2147483647 // 32 位,范围:-2^31 到 2^31 - 1。val long: Long = 9223372036854775807 // 64 位,范围:-2^63 到 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 // Byte

浮点型

FloatDouble 分别具有不同的存储大小和精度。

kotlin">fun main() {val floatValue: Float = 3.14f // 32 位,单精度。val doubleValue: Double = 3.14 // 64 位,双精度。
}

如果使用小数初始化变量,编译器会默认推断为 Double 类型。

kotlin">fun main() {val pi = 3.14 // Double// val one: Double = 1 // errorval oneDouble = 1.0 // Double
}

要显式指定值为 Float 类型,请添加后缀 fF

如果该值包含超过 6-7 位小数,则会被四舍五入。

kotlin">fun main() {val e = 2.7182818284 // Doubleval eFloat = 2.7182818284f // Floatprintln(e) // 2.7182818284println(eFloat) // 2.7182817
}

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

例如,具有 Double 参数的函数只能在 Double 值上调用,而不能在 FloatInt 或其他数值上调用。

必须显式进行类型转换才能将一种数字类型传递给另一种类型的参数。

kotlin">fun printDouble(d: Double) {println(d)
}fun main() {val intValue = 42val floatValue = 3.14f// printDouble(intValue) // 错误:类型不匹配。// printDouble(floatValue) // 错误:类型不匹配。printDouble(intValue.toDouble()) // 正确:显式转换为 Double 类型。printDouble(floatValue.toDouble()) // 正确:显式转换为 Double 类型。
}

数字的字面常量

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

  • 十进制:123

  • 十六进制:0x0F

  • 二进制:0b00001011

  • 不支持八进制

  • Long 类型用大写 L 标记:123L

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

  • 默认是 Double 类型:123.5123.5e10

  • Float 类型用 fF 标记:123.5f

可以使用下划线使数字常量更易读。

kotlin">val oneMillion = 1_000_000
val creditCardNumber = 1234_5678_9012_3456L
val socialSecurityNumber = 999_99_9999L
val hexBytes = 0xFF_EC_DE_5E
val bytes = 0b11010010_01101001_10010100_10010010

JVM 上的数字表示

JVM 平台上,数字存储为原始类型:intdouble 等。

例外情况是,当创建可空数字引用(如 Int?Double? 等)或使用泛型时。数字会被装箱为 JavaIntegerDouble 等。

对相同数字的可空引用可能指向不同的对象。

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
}

由于 JVM-128127 之间的 Integer 应用了内存优化,所有对 a 的可空引用实际上是同一个对象。

这种优化不适用于 b 引用,b 指向不同的对象(因为 10000 超出缓存范围)。

另一方面,它们仍然是相等的。

kotlin">fun main() {val b: Int = 10000println(b == b) // trueval boxedB: Int? = bval anotherBoxedB: Int? = bprintln(boxedB == anotherBoxedB) // true
}

显式数字转换

由于表示方式不同,较小的类型不是较大类型的子类型。如果它们是,我们可能会遇到以下问题。

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">fun main() {val b: Byte = 1val i: Int = b.toInt() // ok// val j: Int = b // error
}

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

  • toByte(): Byte

  • toShort(): Short

  • toInt(): Int

  • toLong(): Long

  • toFloat(): Float

  • toDouble(): Double

在许多情况下,不需要显式转换,因为类型可以从上下文中推断出来,并且算术操作已重载以进行适当的转换。

kotlin">fun main() {val b: Int = 10000val c: Long = b + 1L // ok// val d: Long = b // error
}

运算

整数之间的除法总是返回一个整数。任何小数部分都会被丢弃。

kotlin">fun main() {println(1 + 2) // 3println(2_500_000_000L - 1L) // 2499999999println(3.14 * 2.71) // 8.5094println(3.14 * 2) // 6.28println(3 * 2) // 6println(3.14.toInt() * 2) // 6println(10.0 / 3) // 3.3333333333333335println(10 / 3) // 3println(10 / 3.toDouble()) // 3.3333333333333335println((10 / 3).toDouble()) // 3.0val x = 5L / 2println(x == 2L) // trueprintln(5 % 2) // 1
}

位操作

提供了一组对整数进行位操作的函数。它们直接在二进制级别上操作数字的位表示。

位操作由可以以中缀形式调用的函数表示。它们只能应用于 IntLong 类型。

以下是位操作的完整列表:

  • shl(bits) —— 有符号左移

  • shr(bits) —— 有符号右移

  • ushr(bits) —— 无符号右移

  • and(bits) —— 按位与

  • or(bits) —— 按位或

  • xor(bits) —— 按位异或

  • inv() —— 按位取反

kotlin">val x = (1 shl 2) and 0x00_0F_F0_00

浮点数比较

浮点数操作包括:

  • 相等性检查:a == ba != b

  • 比较操作符:a < ba > ba <= ba >= b

  • 范围实例化和范围检查:a .. bx in a .. bx !in a .. b

kotlin">fun main() {val a = 0.6 / 2println(a == 0.3) // trueprintln(a != 0.31) // trueif (a < 0.31) println("a < 0.31") // a < 0.31if (a <= 0.3) println("a <= 0.3") // a <= 0.3if (a > 0.2999999) println("a > 0.2999999") // a > 0.2999999if (a >= 0.300000) println("a >= 0.300000") // a >= 0.300000
}
kotlin">fun main() {val a = 0.6 / 2if (a in 0.299 .. 0.301) println("a in 0.299 .. 0.301") // a in 0.299 .. 0.301if (a !in 0.399 .. 0.401) println("a !in 0.399 .. 0.401") // a !in 0.399 .. 0.401
}

然而,为了支持通用用例并提供全序关系,对于未静态类型化为浮点数的操作数,行为会有所不同。例如,AnyComparable<...>Collection<T> 类型。在这种情况下,操作使用 FloatDoubleequalscompareTo 实现。因此:

  • NaN 被认为等于自身

  • NaN 被认为大于任何其他元素,包括 POSITIVE_INFINITY

  • -0.0 被认为小于 0.0

以下展示了静态类型化为浮点数的操作数(Double.NaN)和未静态类型化为浮点数的操作数(listOf(T))之间的行为差异。

kotlin">fun main() {// 静态类型化为浮点数的操作数。println(Double.NaN == Double.NaN) // false// 操作数未静态类型化为浮点数,因此 NaN 等于自身。println(listOf(Double.NaN) == listOf(Double.NaN)) // true// 静态类型化为浮点数的操作数。println(0.0 == -0.0) // true// 操作数未静态类型化为浮点数,因此 -0.0 小于 0.0。println(listOf(0.0) == listOf(-0.0)) // false// [-0.0, 0.0, Infinity, NaN]println(listOf(Double.NaN, Double.POSITIVE_INFINITY, 0.0, -0.0).sorted())
}

http://www.ppmy.cn/news/1565132.html

相关文章

PyTorch使用教程(3)-Tensor包

1、张量Tensor 张量(Tensor)是PyTorch深度学习框架中的核心数据结构&#xff0c;在PyTorch软件框架中&#xff0c;几乎所有的数据计算和信息流都是以Tensor的形式在表达。官方给出的定义是&#xff1a; 一个 torch.Tensor是一个包含单个数据类型元素的多维矩阵 关键词 单个数…

React 第二十一节 useDeferredValue 开发中用法注意事项

1、概述 useDeferredValue 是用于延迟渲染视图UI组件的&#xff0c;可以帮助我们管理视图渲染过程中的状态&#xff0c;并提高程序运行的性能&#xff1b; 2、用法 const deferredVal useDeferredValue(params)params: 是我们需要延迟的数据&#xff1b;可以是任何类型的数…

第7章:Python TDD测试Franc对象乘法功能

写在前面 这本书是我们老板推荐过的&#xff0c;我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后&#xff0c;我突然思考&#xff0c;对于测试开发工程师来说&#xff0c;什么才更有价值呢&#xff1f;如何让 AI 工具更好地辅助自己写代码&#xff0c;或许…

基于springboot+thymeleaf+Redis仿知乎网站问答项目源码

项目介绍 基于springbootthymeleafRedis仿知乎网站问答项目源码&#xff0c;可以作为毕业设计项目参考学习 按照需要一定动手能力 发文章&#xff0c;发视频&#xff0c;发想法&#xff0c;提问回答&#xff0c;注册登录 开发环境 使用技术&#xff1a;springbootthymeleafRe…

【Docker】搭建一个功能强大的自托管虚拟浏览器 - n.eko

前言 本教程基于群晖的NAS设备DS423的docker功能进行搭建&#xff0c;DSM版本为 DSM 7.2.2-72806 Update 2。 n.eko 支持多种类型浏览器在其虚拟环境中运行&#xff0c;本次教程使用 Chromium​ 浏览器镜像进行演示&#xff0c;支持访问内网设备和公网地址。 简介 n.eko 是…

C/C++ 时间复杂度(On)

定义&#xff1a; 在计算机科学中&#xff0c;时间复杂性&#xff0c;又称时间复杂度&#xff0c;算法的时间复杂度是一个函数&#xff0c;它定性描述该算法的运行时间。这是一个代表算法输入值的字符串的长度的函数。时间复杂度常用大O符号表述&#xff0c;不包括这个函数的低…

线程池遇到未处理的异常会崩溃吗?

线程池中的 execute 和 submit 方法详解 目录 引言execute 方法 使用示例代码 submit 方法 2.1 提交 Callable 任务2.2 提交 Runnable 任务 遇到未处理异常 3.1 execute 方法遇到未处理异常3.2 submit 方法遇到未处理异常 小结 引言 在多线程编程中&#xff0c;线程池是提高性…

搭建一个基于Spring Boot的外贸平台

搭建一个基于Spring Boot的外贸平台涉及多个模块的开发&#xff0c;包括用户管理、产品管理、订单管理、支付集成、物流跟踪等。以下是一个简化的步骤指南&#xff0c;帮助你快速搭建一个基础的外贸平台。 1. 项目初始化 首先&#xff0c;使用Spring Initializr生成一个Spri…