关于Kotlin的自动类型转换详解

前言

Kotlin 1.4 正式版在好早以前就已经发布了。关于那些“看得见”的新特性,比如SAM转换、参数列表末尾的可选逗号什么的,已经有无数文章介绍过了。所以本文打算介绍一些可能是鲜为人知的、Kotlin 官方团队偷偷塞进 1.4 的新特性。

不过单独讲这些东西会显得文章太过单薄,于是我打算把其他相似的东西拉一起凑凑字数。

本文使用的 Kotlin 版本为 Kotlin 1.4。

本文要讲的东西

看题目就知道了,Kotlin 里自动类型转换(automatic type conversion)。这里讲的不是 「把一个 String 转成 Any,再转成 String」 这种和子类型有关的东西,当然也不是 Smart Cast,而是两个不相容的类型之间的转换,比如说 Int 转成 Long,如下文所示。

数值转换

一般地,在 Kotlin 里我们不能像 Java 一样直接把一个 Int 类型的东西赋值给 Long 类型的变量,因为它们之间并不具有子类型关系。像下面这样会得到一个编译错误:

val int: Int = 555
val long: Long = int // 编译错误!
println(long)

你需要调用标准库提供给你的那些 toXXX 函数把数值转换成其他类型的数值。

val int: Int = 555
val long: Long = int.toLong() // OK
println(long)

Kotlin 官方团队曾经表示过不喜欢隐式(implicit)的东西,关于数值的隐式类型转换也包括在内。这就导致了使用 Kotlin 在进行一些关于数值方面的操作时,有时候会写出一些看起来无比蛋疼的代码。

Bennyhuo:就是有时候写点儿计算比较多的代码,满篇的 toFloat toDouble。

不一般地,我们可以使用 @Suppress 来搞事:

val int: Int = 233
@Suppress("TYPE_MISMATCH")
val long: Long = int
println(long) // 233

这个代码是可以跑起来的,而且你真的可以从字节码里看到那个把 Int 转成 Long 的指令 I2L。

不过我不确定 Kotlin 的其他 target 是否能这样用,我也不保证这样写完全不会出问题。(这里是关于 @Suppress 的免责声明,请读者自行脑补)

SAM Conversion

SAM 转换也是一种自动类型转换。它把一个 lambda 表达式(具有函数类型)转成某个具体的接口类型。

fun interface ISome {
 fun some()
}

fun useSome(some: ISome) {}

useSome { println("some") }

在我的另一篇文章里有更详细的介绍。

如果读者不同意这个说法,可以选择跳过本小节内容。

Coercion to Unit

我们都知道 Kotlin 的 lambda 表达式是使用里面最后一个表达式的值来作为 lambda 的返回值的。比如这样:

val block = { "yeah" }

block 的类型是 () -> String。

然后我们来看看这样的情况:

fun test(block: () -> Unit) {
 println(block())
}

test { "yeah" } // 输出 Unit

相信很多人都熟悉这样的写法。

在某些初学者的眼里这看起来像是把一个 () -> String 类型的 lambda 传给了需要 () -> Unit 类型的函数。

这就是 coercion to unit,一个很久以前就存在的特性,可以理解为编译器自动帮你在 lambda 表达式的最后加了一行 Unit,把本来应该是() -> String 类型的 lambda 变成了 () -> Unit 类型。

在 Kotlin 1.4 版本,这个特性得到了进化,你甚至可以这样写:

fun test(block: () -> Unit) {
 println(block())
}

fun some(): String {
 return "str"
}

// 需要 Kotlin 1.4 版本
test(::some) // 输出 Unit

编译器帮你把 () -> String 类型的函数引用转成了 () -> Unit。

Unit Conversion

警告:这是一项未完成的特性!

添加编译器参数 -XXLanguage:+UnitConversion,你就开启了一个 Kotlin 官方团队偷偷塞进 1.4 版本的未完成的新特性。

这个特性允许你写出这样的代码:

fun test(block: () -> Unit) {
 println(block())
}

fun some(block: () -> String) {
 test(block) // 这里是重点
 // 如果你不加那个编译器参数,会报错
}

fun main() {
 some { "str" }
 // 理论上会输出 Unit
}

在函数 some 里把一个 () -> String 传给了 test 函数,可以看出来这个特性其实和 coercion to unit 是差不多的。

理论上这样的代码运行时会输出 Unit,但是目前由于该特性的代码生成没写好,得不到预期的结果。

另外,在开启了这个特性后,() -> String 并不会成为 () -> Unit 的子类型,它们依然是两个不相容的类型。

Suspend Conversion

警告:这是一项未完成的特性!

这是本文要介绍的第二个 Kt 官方团队偷偷塞进 1.4 版本的未完成的新特性。

比如说我们有这样的一个函数:

fun test(f: suspend () -> Unit) {
 // do something with f
}

我们可以这样调用它:

test { println("hi") }

但是这样不行:

val f = { println("hi") }
test(f) // 编译错误

编译器会告诉你类型不匹配,f 是 () -> Unit 类型,test 函数需要 suspend () -> Unit 类型的参数。

当你添加了编译器参数 -XXLanguage:+SuspendConversion,就可以让上面的代码通过编译。

也就是说这个特性可以帮你把普通函数类型的值转成 suspend 函数类型。

当然由于这是未完成的功能,即使可以通过编译,但是跑起来还是会炸。

这个特性或许会在 Kotlin 1.5 版本完工,但请不要抱有期待。

结尾

我并不想讨论「为什么要加这种奇怪的特性」之类的话题。

不可否认的是,在有限的程序员生涯中,这些新特性可能一次也用不上。上面提到的问题也都有相应的 workaround,不需要新特性也可以写出等价的代码,就是没有那么优雅罢了(

到此这篇关于关于Kotlin自动类型转换的文章就介绍到这了,更多相关Kotlin自动类型转换内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Kotlin类型安全构建器的一次运用记录

    在android官方指导的相关应用框架中,用到一个Resource类来表示网络请求的状态与结果 // A generic class that contains data and status about loading this data. sealed class Resource<T>( val data: T? = null, val message: String? = null ) { class Success<T>(data: T) : Resource<T&

  • Kotlin教程之基本数据类型

    Kotlin教程之基本数据类型的学习 基本类型 我们知道java的基本类型分为基本数据类型: int 普通整型 float 浮点型 char 字符型 short 短整型 byte 字节型 long 长整型 double 双精度 boolean 布尔型 和引用数据类型: class Type 类类型 interface Type 接口类型 array Type 数组类型 null Type 空类型 在 Kotlin 中,所有变量的成员方法和属性都是一个对象.一些类型是内建的,因为它们的实现是优化过

  • Kotlin基本类型自动装箱出现问题解决办法

    Kotlin基本类型自动装箱出现问题解决办法 问题 在Kotlin官方文档介绍基本类型时,给我们说明了在有些情况下会对基本类型自动进行装箱操作. 但是具体是如何进行装箱,以及何时进行装箱缺没有提供详细介绍.只是提供了一个例子,如下: val a: Int = 10000 print(a === a) // Prints 'true' val boxedA: Int? = a val anotherBoxedA: Int? = a print(boxedA === anotherBoxedA) /

  • Kotlin基本类型自动装箱一点问题剖析

    问题 在Kotlin官方文档介绍基本类型时,给我们说明了在有些情况下会对基本类型自动进行装箱操作. 但是具体是如何进行装箱,以及何时进行装箱缺没有提供详细介绍.只是提供了一个例子,如下: val a: Int = 10000 print(a === a) // Prints 'true' val boxedA: Int? = a val anotherBoxedA: Int? = a print(boxedA === anotherBoxedA) // !!!Prints 'false'!!!

  • Kotlin类型系统竟如此简单

    Quote 在学习 Kotlin 的过程中,对 Kotlin 的类型系统产生了好奇,Kotlin 是否存在类似于 Java 中 Object 的公共基类?Kotlin 中是否也有类似于 Java 基础类型这样的单独分支?在研究一番过后,博主发现相较于 Java,Kotlin 交出了更为满意的答案,而且出乎意外地简单,只需要遵循简单的规则,便能理解整个类型系统. Any Any 等同于 Java 中的 Object 的概念,Any 在注释中这么写到: The root of the Kotlin

  • Kotlin基础教程之数据类型

    Kotlin基础教程之数据类型 一切都是对象. 在Kotlin中一切都是对象.Kotlin有一些基本类型Boolean,Byte,Shot,Int,Long,Float,Double 在Kotlin中没有原始类型,以上这些类型都是对象,比如 运行结果如下 Kotlin支持16进制字面值,二进制字面值和科学记数法,官方文档中没有提到8进制 所有基本的几个数据类型不会进行显式类型转换,这一点尤为重要!那么如何进行转换呢?如下: 可以使用toLong,toInt,toShort,toChar等函数进行

  • 关于Kotlin的自动类型转换详解

    前言 Kotlin 1.4 正式版在好早以前就已经发布了.关于那些"看得见"的新特性,比如SAM转换.参数列表末尾的可选逗号什么的,已经有无数文章介绍过了.所以本文打算介绍一些可能是鲜为人知的.Kotlin 官方团队偷偷塞进 1.4 的新特性. 不过单独讲这些东西会显得文章太过单薄,于是我打算把其他相似的东西拉一起凑凑字数. 本文使用的 Kotlin 版本为 Kotlin 1.4. 本文要讲的东西 看题目就知道了,Kotlin 里自动类型转换(automatic type conver

  • Kotlin 基础语法实例详解

    Kotlin 基础语法实例详解 包 定义和引入Java一样,在文件开头, 行结束不需要" ; " package com.test.hello import android.os.Bundle 变量 只读变量,val 开头,初始化后不能再赋值,相当于Java的 final 变量 val a: Int = 1 val b = 1 //类型自动推断为Int val c: Int //没有初始化时必须指定类型 c = 1 //初始化 可变变量, var 关键字开头 var x = 10 x

  • Kotlin null的处理详解

    Kotlin null的处理详解 NullPointerException,俗称NPE,不管菜鸟还是老鸟们,都是不可避免,经常遇到的一个异常,解释起来很简单,就"空指针"三个字.总是在一次不小心,而掉进这个陷阱里.Kotlin 的设计目标就是希望消除代码中 null 引用带来的危险, 也就是所谓的造成十亿美元损失的大错误. NPE的原因 尽管Kotlin希望消除代码中的NPE,我们总是不小心,总会不小心又掉进NPE的陷阱,下面是可能NPE的原因: 明确调用 throw NullPoin

  • Kotlin 的注解类详解及实例

    Kotlin 的注解类详解及实例 注解声明 注解是将元数据附加到代码的方法.要声明注解,请将 annotation 修饰符放在类的前面: annotation class Fancy 注解的附加属性可以通过用元注解标注注解类来指定: @Target 指定可以用 该注解标注的元素的可能的类型(类.函数.属性.表达式等): @Retention 指定该注解是否 存储在编译后的 class 文件中,以及它在运行时能否通过反射可见 (默认都是 true): @Repeatable 允许 在单个元素上多次

  • JavaScript显式数据类型转换详解

    基本概念 将值从一种类型转换为另一种类型称为类型转换,类型转换总是返回基本类型值,如字符串.数字和布尔值,不会返回引用类型值. 类型转换分为"显式"和"隐式":"显式"转换发生在静态类型语言的编译阶段,而"隐式"转换则发生在动态类型语言的运行时. 显式类型转换 非字符串到字符串的类型转换 toString() 方法 数字.布尔值.字符串和对象都有 toString() 方法,但 null 和 undefined 没有. 例子:

  • C/C++中数据类型转换详解及其作用介绍

    目录 概述 不同类型数据间的转换 隐式类型转换 强制类型转换 自己声明的类型转换 转换构造函数 类型转换函数 案例 应用 概述 在日常的开发中, 我们经常会用到数据类型转换, 所以我们要对数据类型转换有一定的了解. 不同类型数据间的转换 在 C++ 中, 某些标准类型的数据之间可以自动转换. 隐式类型转换 隐式类型转换: 由 C++ 编译系统自动完成的, 我们无需干预. 例如: int main() { int a = 6; a = a + 3.5; cout << a << en

  • Python教程之类型转换详解

    目录 隐式类型转换 显式类型转换 Python 定义了类型转换函数以将一种数据类型直接转换为另一种数据类型,这在日常和竞争性编程中很有用.本文旨在提供有关某些转换函数的信息. Python中有两种类型转换: 隐式类型转换 显式类型转换 让我们详细讨论它们. 隐式类型转换 在 Python 中数据类型的隐式类型转换中,Python 解释器会自动将一种数据类型转换为另一种数据类型,而无需任何用户参与.要更清楚地了解该主题,请参阅以下示例. 例子: x = 10 print("x is of type

  • JAVA中string数据类型转换详解

    在JAVA中string是final类,提供字符串不可以修改,string类型在项目中经常使用,下面给大家介绍比较常用的string数据类型转换: String数据类型转换成long.int.double.float.boolean.char等七种数据类型 复制代码 代码如下: * 数据类型转换 * @author Administrator * */ public class 数据类型转换 { public static void main(String[] args) { String c=

  • Kotlin 单例实例详解

    Kotlin 单例实例详解 单例的实现方法,可以通过同伴对象,或者 lazy. 示例: class Hello private constructor() { companion object { val instance = Hello() } } 通过 lazy 实现 class Hello private constructor() { private object Holder { val INSTANCE = Hello() } companion object { val insta

  • 有关C++继承与友元、继承与类型转换详解

    实例如下: #include <iostream> using namespace std; class a{ friend class pal; private: int i; protected: int j; public: int k; }; class b:public a{ protected: int l; }; class c:protected a{}; class d:private a{}; class e:public b{}; class pal{ public: v

随机推荐