Kotlin 基本数据类型(一):Numbers

Kotlin 基本数据类型(一):概述及分类

整数类型

Kotlin 内置了几种类型来表示数字,有以下几种类型来表示整数:

Type Size (bits) Min value Max value
Byte 8 -128 127
Short 16 -32768 32767
Int 32 -2,147,483,648 2,147,483,647
Long 64 -9,223,372,036,854,775,808 9,223,372,036,854,775,807

如果在初始化变量的时候,没有指定具体的类型。那么编译器会自动推断类型,只不过会从Int类型开始往上推断。如果说我们声明的变量超过了Int的最小值和最大值,那么就会推断为Long。如果没有超过Int类型的范围,那就是Int。

所以,只要我们声明的整数在-2,147,483,648 到 2,147,483,647范围内,只要没指定类型,那就是Int类型。

如果我们想要明确为Long类型,只需要在数字后面加上L就行。如果我们想要使用 Byte 或者 Short 类型,就需要在声明的时候明确声明出类型来。

只要我们明确声明了类型,就会触发编译器检查我们给的数字是否在类型的范围内。

kotlin 复制代码
val one = 1 // Int  没有明确类型,推断为Int
val threeBillion = 3000000000 // Long 超过了Int的范围,推断为Long
val oneLong = 1L // Long 添加L后缀,强制为Long
val oneByte: Byte = 1 // 声明为Byte,强制为Byte

浮点类型

对于实数来说,Kotlin 提供了遵循 IEEE 754 标准的浮点类型 Float 和 Double,Float是单精度,Double是双精度。

两种类型的比较如下:

Type Size (bits) Significant bits Exponent bits Decimal digits
Float 32 24 8 6-7
Double 64 53 11 15-16

简单来说:Float类型能表示的十进制有效数字位数大概是 6 - 7 位;Double类型能表示的十进制有效数字位数大概是 15 - 16 位

对我们开发者来说,我们只能用小数来初始化 Double 和 Float 类型的变量,并且编译器类型推断小数是Double类型。

kotlin 复制代码
val pi = 3.14          // Double

val one: Double = 1    // Int is inferred  编译器会报错 只能使用小数来初始化Double
// Initializer type mismatch

val oneDouble = 1.0    // Double  编译器推断为Doube

如果我们开发者想要声明Float类型,那么只需要在小数后面加上f或者F即可,如果一个超过7为小数的变量,被强制声明为F,那么精度会缺失:

kotlin 复制代码
val e = 2.7182818284          // Double
val eFloat = 2.7182818284f    // Float, actual value is 2.7182817
// 2.7182818284小数超过了7位,强制为Float类型,那么真实的数值就会变成2.7182817。

和其他编程语言不同,Kotlin没有隐式的类型转换。在其他语言中,小范围的数字可以放在大范围的数字里面,比如int到long。

但在Kotlin中,这是不允许的。如果一个方法接受一个Doube类型的参数,那么只可以传入Doube,传入Float,Int或者其他数字类型都是不被允许的。

kotlin 复制代码
fun printDouble(x: Double) { print(x) }

val x = 1.0
val xInt = 1    
val xFloat = 1.0f 

printDouble(x) //正确,x推断为Doubel

printDouble(xInt)   //错误,xInt推断为Int
// Argument type mismatch

printDouble(xFloat)//错误,xFloat强制为Float
// Argument type mismatch

number字面常量

整型值的字面常量有以下几种类型:

  • 十进制:123
  • 长整数以大写字母 L 结尾:123L
  • 十六进制:0x0F
  • 二进制:0b00001011

Kotlin 也支持浮点数的常规表示法:

  • 双精度浮点数(当小数部分不以字母结尾时默认为此类型):123.5、123.5e10
  • 单精度浮点数,以字母 f 或 F 结尾: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 
val bigFractional = 1_234_567.7182818284

JVM 数字类型装箱和缓存

因为 JVM 会缓存字节大小的数字,所以有一些代码可能会和开发者预期有所不同。

JVM 会将数字存储类原始的类型:int、doubel等等。但是假如我们使用了泛型或者创建了可空的数字引用比如Int?,数字会被包装为包装类型,比如Integer。

JVM 对表示 -128 到 127 之间数字的 Integer 及其他数值对象采用了一种内存优化技术。所有指向这类对象的可空引用都会指向同一个缓存对象。例如,以下代码中的可空对象在引用上是相等的:

kotlin 复制代码
val a: Int = 100
val boxedA: Int? = a
val anotherBoxedA: Int? = a

println(boxedA === anotherBoxedA) // true

如果超过了-128到127,可空对象的引用就是不用的了,但是结构是相同的。

kotlin 复制代码
val b: Int = 10000
val boxedB: Int? = b
val anotherBoxedB: Int? = b

println(boxedB === anotherBoxedB) // false  超过了范围,所以地址不用
println(boxedB == anotherBoxedB) // true   超过了范围,结构相同,也就是equal相等。

鉴于此,当我们开发者使用包装类型和字面类型进行比较的时候,就会有警告信息: "Identity equality for arguments of types ... and ... is prohibited.",所以在比较Int, Short, Long, 和 Byte 类型 (包括 Char 和 Boolean)时,使用结构性相等比较会有更加稳定的结果。

显示转换

由于表示方式不同,数字类型之间不存在父子类型关系。因此,较小的类型不会隐式转换为较大的类型,反之亦然。例如,将 Byte 类型的值赋给 Int 变量需要显式转换:

kotlin 复制代码
val byte: Byte = 1   //确定为byte类型
// OK, literals are checked statically

val intAssignedByte: Int = byte  //编译器报错  小类型无法给大类型
// Initializer type mismatch

val intConvertedByte: Int = byte.toInt()  //显示的类型转换

println(intConvertedByte)

数字类型之间支持相互显示转换:

toByte(): Byte ( Float and Double 已经弃用)

toShort(): Short

toInt(): Int

toLong(): Long

toFloat(): Float

toDouble(): Double

不过,在很多情况下,不需要进行显式类型转换,因为类型会从上下文推断得出,而且算术运算符经过重载可以自动处理转换。例如:

kotlin 复制代码
val l = 1L + 3       // Long + Int => Long
println(l is Long)   // true

不支持隐式转换的考量

Kotlin 不支持隐式转换是因为防止出现意外的行为。

如果不同类型可以隐式转换,可能有的时候比较相等会出现意想不到的结果。比如,int和long。

kotlin 复制代码
val a: Int? = 1   
val b: Long? = a   
print(b == a)      // Prints "false" 

因为Long在比较的时候,会先比较是否是Long类型

number 运算

Kotlin支持标准的数字运算:+, -, *, /, %,这些运算被包装成了成员函数。

kotlin 复制代码
println(1 + 2)
println(2_500_000_000L - 1L)
println(3.14 * 2.71)
println(10.0 / 3)

当然了,开发者也可以在自定义的数字类中重载这些运算。

整数除法

整数之前相除得到的结果也是整数,小数部分会被舍弃。

kotlin 复制代码
val x = 5 / 2
println(x == 2.5) 
// Operator '==' cannot be applied to 'Int' and 'Double'

println(x == 2)   
// true

想要得到小数的结果,只需要两个整数中的任意一个变成浮点数即可。

位运算

Kotlin 为整数提供了一系列的位运算操作,这些运算直接对数字表示的二进制位进行操作。位运算使用中缀形式的函数来表示。

kotlin 复制代码
fun main() {
//sampleStart
    val x = 1
    val xShiftedLeft = (x shl 2)
    println(xShiftedLeft)
    // 4

    val xAnd = x and 0x000FF000
    println(xAnd)
    // 0
//sampleEnd
}
  • shl(bits) -- 带符号左移
  • shr(bits) -- 带符号右移
  • ushr(bits) -- 无符号右移
  • and(bits) -- 按位与
  • or(bits) -- 按位或
  • xor(bits) -- 按位异或
  • inv() -- 按位取反

浮点数比较

本节讨论的浮点数操作包括:

相等性检查: a == b and a != b

比较运算: a < b, a > b, a <= b, a >= b

范围实例和检查: a..b, x in a..b, x !in a..b

当操作数 a 和 b 在静态类型上明确为 Float 或 Double,或它们的可空对应类型(类型已声明、推断或为智能类型转换的结果)时,这些数字上的操作及其形成的范围遵循 IEEE 754 浮点数算术标准。

然而,为了支持泛型场景并提供全序关系,对于静态类型非浮点数的操作数(例如 Any、Comparable<...> 或 Collection 类型),行为会有所不同。在这种情况下,操作会使用 Float 和 Double 的 equals 和 compareTo 实现。因此:

  • NaN 被视为与自身相等
  • NaN 被视为大于任何其他元素,包括 POSITIVE_INFINITY(正无穷)
  • 0.0 被视为小于 0.0

下面的示例展示了静态类型为浮点数的操作数(Double.NaN)与静态类型非浮点数的操作数(listOf(T))之间的行为差异:

kotlin 复制代码
    // Operand statically typed as floating-point number
println(Double.NaN == Double.NaN)                 // false  

//Double.NaN == Double.NaN 结果为 false,是因为 NaN(Not a Number,非数值)在 IEEE 754 浮点数标准中的特殊定义。
//IEEE 754 标准明确规定:NaN 与任何值(包括它自身)都不相等。这是因为 NaN 代表 "无效的数值结果"(例如 0/0、sqrt (-1) 等运算的结果),而逻辑上 "两个无效结果是否相等" 是无法定义的。因此,标准强制规定 NaN 与自身的相等性检查返回 false。

// Operand NOT statically typed as floating-point number
// So NaN is equal to itself
println(listOf(Double.NaN) == listOf(Double.NaN)) // true
//基本类型的 NaN 比较遵循 IEEE 754 标准(不等),而包装类型的 NaN 通过 equals() 比较时被视为相等,列表比较依赖后者,因此结果为 true

// Operand statically typed as floating-point number
println(0.0 == -0.0)                              // true

// Operand NOT statically typed as floating-point number
// So -0.0 is less than 0.0
println(listOf(0.0) == listOf(-0.0))              // false

println(listOf(Double.NaN, Double.POSITIVE_INFINITY, 0.0, -0.0).sorted())
// [-0.0, 0.0, Infinity, NaN]

原文链接

相关推荐
GISer_Jing3 分钟前
前端GIS篇——WebGIS、WebGL、Java后端篇
java·前端·webgl
excel11 分钟前
Vue3 EffectScope 源码解析与理解
前端·javascript·面试
不老刘1 小时前
Base UI:一款极简主义的「无样式」组件库
前端·ui
祈祷苍天赐我java之术1 小时前
Redis 有序集合解析
java·前端·windows·redis·缓存·bootstrap·html
maki0772 小时前
VR大空间资料 03 —— VRGK使用体验和源码分析
android·vr·虚幻·源码分析·oculus·htc vive·vrgk
ObjectX前端实验室2 小时前
【react18原理探究实践】React Effect List 构建与 Commit 阶段详解
前端·react.js
用户1456775610373 小时前
文件太大传不了?用它一压,秒变合格尺寸!
前端
用户1456775610373 小时前
再也不用一张张处理了!批量压缩神器来了,快收藏
前端
心.c3 小时前
一套完整的前端“白屏”问题分析与解决方案(性能优化)
前端·javascript·性能优化·html
white-persist3 小时前
【burp手机真机抓包】Burp Suite 在真机(Android and IOS)抓包手机APP + 微信小程序详细教程
android·前端·ios·智能手机·微信小程序·小程序·原型模式