【Kotlin】上手学习之类型篇

一、类型

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。 显式指定类型会触发编译器检测该值是否超出指定类型的表示范围。

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 或者其他数字值调用:

在这里插入图片描述

运行之后会报错提示:

/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运行类似上面那串代码的话,就可以正常运行

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 等。

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

fun main() {
    val a: Int = 100
    val boxedA: Int? = a
    val anotherBoxedA: Int? = a

    val b: Int = 10000
    val boxedB: Int? = b
    val anotherBoxedB: Int? = b

    println(boxedA === anotherBoxedA) // true
    println(boxedB === anotherBoxedB) // false
}

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

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

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

Integer a = 127;
Integer b = 127;
System.out.println(a == b); // true

Integer e = 128;
Integer f = 128;
System.out.println(e == f); // false

Integer c = new Integer(128);
Integer d = new Integer(128);
System.out.println(c == d); // false
数据的比较

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

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

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

// 假想的代码,实际上并不能编译:
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变量必须显式转换:

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

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

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

1.1.2 布尔

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

布尔值的内置运算有:

  • ||——(逻辑
  • &&——(逻辑
  • !——(逻辑
fun main() {
    val myTrue: Boolean = true
    val myFalse: Boolean = false
    val boolNull: Boolean? = null

    println(myTrue || myFalse)
    // true
    println(myTrue && myFalse)
    // false
    println(!myTrue)
    // false
    println(boolNull)
    // null
}

1.1.3 字符

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

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

1.1.4 字符串

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

val str = "abcd 123"

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

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

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

字符串模板

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

fun main() {
    val i = 10
    println("i = $i") 
    // i = 10

    val letters = listOf("a","b","c","d","e")
    println("Letters: $letters") 
    // Letters: [a, b, c, d, e]
}

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

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() 函数并将项目值传递给它:
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 元素:
fun main() {
    // Creates an array with values [null, null, null]
    val nullArray: Array<Int?> = arrayOfNulls(3)
    println(nullArray.joinToString())
    // null, null, null
}
  1. 此示例使用emptyArray()函数创建一个空数组:
    var exampleArray = emptyArray<String>()
Array构造函数创建数组

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

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
}
嵌套数组

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

fun main() {
    // Creates a two-dimensional array
    val twoDArray = Array(2) { Array<Int>(2) { 0 } }
    println(twoDArray.contentDeepToString())
    // [[0, 0], [0, 0]]

    // Creates a three-dimensional array
    val 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]]]
}
数组求和
fun main() {
    val sumArray = arrayOf(1, 2, 3)

    // Sums array elements
    println(sumArray.sum())
    // 6
}
将数组转换为集合
转换为 List 或 Set

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

fun main() {

    val simpleArray = arrayOf("a", "b", "c", "c")

    // Converts to a Set
    println(simpleArray.toSet())
    // [a, b, c]

    // Converts to a List
    println(simpleArray.toList())
    // [a, b, c, c]

}
转换为 Map
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 first
    println(pairArray.toMap())
    // {apple=140, banana=150, cherry=90}

}
原生类型数组

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

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

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

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

fun main() {
    // Creates an array of Int of size 5 with the values initialized to zero
    val 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 在运行时检测对象是否符合给定类型:

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-检测以及显式转换,并在必要时自动插入(安全的)转换:

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

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

if (x !is String) return

print(x.length) // x 自动转换为字符串

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

// `||` 右侧的 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循环也一样:

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

val x: String = y as String

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

val x: String? = y as String?

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

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

val x: String? = y as? String

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

比如下面这段代码

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

运行之后会抛出异常

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')

如果换成可空的话

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

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

str is null

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

迅为RK3576开发板Android 多屏显示

迅为iTOP-3576开发板采用瑞芯微RK3576高性能、低功耗的应用处理芯片&#xff0c;集成了4个Cortex-A72和4个Cortex-A53核心&#xff0c;以及独立的NEON协处理器。它适用于ARM PC、边缘计算、个人移动互联网设备及其他多媒体产品。 1.1 Android 多屏同显 iTOP-RK3576 开发板支持…

解决关于Xcode16提交审核报错

# 问题描述 The following issues occurred while distributing your application. Asset validation failed Invalid Executable. The executable xxx.app/Frameworks/HappyDNS.framework/HappyDNS contains bitcode.(lD:ef5dd249-731f-4731-8173-8e4a12519352) Asset valida…

语义检索效果差?深度学习rerank VS 统计rerank选哪个

前段时间我开发了一个用白话文搜索语义相近的古诗词的应用&#xff08;详见&#xff1a;《朋友圈装腔指南&#xff1a;如何用向量数据库把大白话变成古诗词》&#xff09;&#xff0c;但是有时候搜索结果却不让人满意&#xff0c;排名靠前的结果和查询的语义没啥关系&#xff0…

Linux 常用命令 - chmod 【改变文件或目录权限】

简介 “chmod” 这个命令来自于 “change mode” 的缩写&#xff0c;用于更改文件或目录的访问权限。这个命令允许用户设定谁可以读取、写入或执行一个文件。在 Linux 和其他类 Unix 系统中&#xff0c;文件权限对系统安全和用户隐私至关重要。 Linux/Unix 的文件调用权限分为…

基于机器学习的电信用户流失预测与数据分析可视化

完整源码项目包获取→点击文章末尾名片&#xff01; 背景描述 根据IBM商业社区分享团队描述&#xff0c;该数据集为某电信公司在加利福尼亚为7000余位用户&#xff08;个人/家庭&#xff09;提供电话和互联网服务的相关记录。描述用户基本情况&#xff0c;包括每位用户已注册的…

【Sql递归查询】Mysql、Oracle、SQL Server、PostgreSQL 实现递归查询的区别与案例(详解)

文章目录 Mysql 5.7 递归查询Mysql 8 实现递归查询Oracle递归示例SQL Server 递归查询示例PostgreSQL 递归查询示例 更多相关内容可查看 Mysql 5.7 递归查询 MySQL 5.7 本身不直接支持标准 SQL 中的递归查询语法&#xff08;如 WITH RECURSIVE 这种常见的递归查询方式&#xf…

接上篇基于Alertmanager 配置钉钉告警

Alertmanager 是一个用于处理和管理 Prometheus 警报的开源工具。它负责接收来自 Prometheus 服务器的警报&#xff0c;进行去重、分组、静默、抑制等操作&#xff0c;并通过电子邮件、PagerDuty、Slack 等多种渠道发送通知。 主要功能 去重&#xff1a;合并相同或相似的警报&…

Qt应用之MDI(多文档设计)

qt creator 版本6.8.0 MinGW 64bit 由此模块可以扩展成设计一个qt文本编辑器。 界面如下 部分功能展示如下 新建文件 打开文件 mdi模式、级联模式和平铺模式 界面和程序构建过程。 1.如图所需.cpp和.h文件 2.mainwindow.ui和tformdoc.ui界面布局如下 不懂什么是Action如何…

软件授权管理中的软件激活向导示例

软件激活向导示例 在软件许可中&#xff0c;提供许可应该是简单和安全的。这适用于想要在中央许可证服务器上创建新许可证的软件开发人员&#xff0c;也适用于需要在其设备上获得许可证的最终用户。如果所讨论的系统有互联网连接&#xff0c;或是暂时的连接&#xff0c;就可以…

GB44495-2024 汽车整车信息安全技术要求 - V2X部分前置要求

背景 GB 44495-2024《汽车整车信息安全技术要求》中关于V2X&#xff08;车与外界通信&#xff09;的部分&#xff0c;主要关注于通信安全要求&#xff0c;旨在确保车辆在与外部设备进行数据交互时的信息安全。其测试大致可分为消息层&#xff08;数据无异常&#xff09;、应用…

phpstudy靶场搭建问题

前言&#xff1a; 靶场搭建遇到的问题&#xff0c;记录一下&#xff0c;可能是基础不牢吧&#xff0c;老是遇到奇奇怪怪的问题 思路&#xff1a; 跟着网上的搭建走一遍 内容&#xff1a; 目录 搭建pikachu遇到的问题 搭建pikachu遇到的问题 其实并不是第一次搭建&#x…

【Excel】【VBA】双列排序:坐标从Y从大到小排列之后相同Y坐标的行再对X从小到大排列

Excel VBA 双列排序 功能概述 这段VBA代码实现了Excel中的双列排序功能&#xff0c;具体是&#xff1a; 跳过前3行表头先按C列数据从大到小排序在C列值相同的情况下&#xff0c;按B列从大到小排序排序时保持整行数据的完整性 流程图 #mermaid-svg-XJERemQluZlM4K8l {font-fa…

Hive SQL必刷练习题:留存率问题

首次登录算作当天新增&#xff0c;第二天也登录了算作一日留存。可以理解为&#xff0c;在10月1号登陆了。在10月2号也登陆了&#xff0c;那这个人就可以算是在1号留存 今日留存率 &#xff08;今日登录且明天也登录的用户数&#xff09; / 今日登录的总用户数 * 100% 解决思…

C++基础入门(二)

目录 前言 一、重载 1.函数重载 2.运算符重载 二、构造函数 1.什么是构造函数 2.带参数的构造函数 3.使用初始化列表 4.this关键字 5.new关键字 三、析构函数 1.什么是析构函数 四、静态成员变量 1.静态成员的定义 2.静态成员变量的作用 五、继承 1.继承基本概…

Redis 中 TTL 的基本知识与禁用缓存键的实现策略(Java)

目录 前言1. 基本知识2. Java代码 前言 &#x1f91f; 找工作&#xff0c;来万码优才&#xff1a;&#x1f449; #小程序://万码优才/r6rqmzDaXpYkJZF 单纯学习Redis可以看我前言的Java基本知识路线&#xff01;&#xff01; 对于Java的基本知识推荐阅读&#xff1a; java框架…

基于unity的多人家装应用的设计与实现

摘要 本课题根据主流家装应用存在的问题和结合了Unity3D引擎所具有的优势&#xff0c;在主流家装应用的基础上弥补了常见的缺憾&#xff0c;实现了一种新型的交互更强的家装展示系统。 本系统主要通过将家具模型资源和材质等资源导入Unity3D平台中&#xff0c;通过C#代码开发&a…

Three.js+Vue3+Vite应用lil-GUI调试开发3D效果(三)

前期文章中我们完成了创建第一个场景、添加轨道控制器的功能&#xff0c;接下来我们继续阐述其他的功能&#xff0c;本篇文章中主要讲述如何应用lil-GUI调试开发3D效果&#xff0c;在开始具体流程和步骤之前&#xff0c;请先查看之前的内容&#xff0c;因为该功能必须在前期内容…

采用海豚调度器+Doris开发数仓保姆级教程(满满是踩坑干货细节,持续更新)

一、采用海豚调度器Doris开发平替CDH Hdfs Yarn Hive Oozie的理由。 海豚调度器Doris离线数仓方案与CDH Hive在多个方面存在显著差异&#xff0c;以下是对这两种方案的对比分析&#xff1a; 1. 架构复杂性 CDH Hive&#xff1a;基于Hadoop生态&#xff0c;组件众多&#…

50.【8】BUUCTF WEB HardSql

进入靶场 随便输输 上order by ????????&#xff0c;被过滤了,继续找其他也被过滤的关键字 #&#xff0c;-- -&#xff0c;-- 都不行&#xff0c;尝试其他特殊字符后发现and&#xff0c;union&#xff0c;select&#xff0c;空格&#xff0c;都被过滤了 如下 我就不知…

Redis 3.2.1在Win10系统上的安装教程

诸神缄默不语-个人CSDN博文目录 这个文件可以跟我要&#xff0c;也可以从官网下载&#xff1a;https://github.com/MicrosoftArchive/redis/releases 这个是微软以前维护的Windows版Redis安装包&#xff0c;如果想要比较新的版本可以从别人维护的项目里下&#xff08;https://…