当前位置: 首页 > news >正文

解析Kotlin中的Nothing【笔记摘要】

1.Nothing的本质

Nothing 的源码很简单:

public class Nothing private constructor()

可以看到它是个class,但它的构造函数是 private 的,这就导致我们没法创建它的实例,并且在源码里 Kotlin 也没有帮我们创建它的实例。

基于这样的前提,当我们写出这个函数声明的时候:

fun nothing(): Nothing {}

我们不可能找到一个合适的值来返回,因为压根找不到Nothing的实例

2.作用一:作为函数「永不返回」的提示

上面的例子看起来是个悖论,但它其实就是 Nothing 存在的意义:它找不到任何可用的值,所以,以Nothing为返回值类型的函数一定是个不会返回的函数。

比如它可以总是抛异常:

fun nothing() : Nothing {throw RuntimeException("Nothing!")
}

这个写法并没有返回任何结果,而是抛异常了,所以是合法的。

注意:抛异常是可以忽略返回值,而且这不是 Nothing 的特性,在Java和Kotlin中都是如此:

//Java
public String getName() {throw new NullPointerException("不能为空!");
}//Kotlin
fun getName() : String {throw NullPointerException("不能为空!")
}

上面这个getName()看起来有点奇怪,为什么在一个函数中就只是抛出异常呢,但它的写法本身是完全合法的。如果把函数的名字改一下,再加个注释:

/**当遇到姓名为空的时候,请调用这个函数来抛异常
*/
fun throwOnNameNull() : String {throw NullPointerException("姓名不能为空!")
}

这就很合理了吧?不干别的,就只是抛异常。这是一种很常用的工具函数的写法,包括 Kotlin 和 Compose 的官方源码里也有这种东西。

那么我们继续来看它的返回值类型:我都不返回了,就没必要还写 String 了吧?那写什么?可以把它改成 Unit,

fun throwOnNameNull() : Unit {throw NullPointerException("姓名不能为空!")
}

甚至可以把它改成不写返回值。但这里不写返回值,实际上返回类型是Unit,Kotlin会自动帮我们加上,可以看这篇文章:解析Kotlin中的Unit【笔记摘要】

fun throwOnNameNull() : Unit {throw NullPointerException("姓名不能为空!")
}fun throwOnNameNull() {throw NullPointerException("姓名不能为空!")
}

不过,Kotlin 又进了一步,提供了一个额外的选项:你还可以把它改成 Nothing:

/**当任何变量为空的时候,请统一调用这个函数来抛异常
*/
fun throwOnNameNull() : Nothing {throw NullPointerException("姓名不能为空!")
}

虽然我找不到 Nothing 的实例,但是这个函数本来就是永远抛异常的,找不到实例也没关系。

这么写的价值就在于,Nothing 这个返回值类型能够给使用它的开发者一个明确的提示:这是个永远不会返回的函数。这种提示本身,就会给开发提供一些方便,它能很好地避免函数的调用者对函数的误解而导致的一些问题。

Kotlin 的源码、Compose 的源码里都有不少这样的实践,比如 Compose 的 noLocalProviderFor() 函数:

private fun noLocalProvidedFor(name: String): Nothing {error("CompositionLocal $name not present")
}

拓展:其实 Nothing 的永不返回特性,除了抛异常之外,还有一种场景,就是无限循环:

fun foreverRepeat(): Nothing {while (true) {...}
}

不过一般很少有人这么去用,大部分都是用在抛异常的场景

3.作用二:作为泛型对象的临时空白填充

另外 Nothing 除了「没有可用的实例」之外,还有个特性:它是所有类型共同的子类型

不过,这个特性又有什么作用呢?它就能让你对于任何变量的赋值,都可以在等号右边写一个 Nothing

val nothing: Nothing = ???
var apple: Apple = nothing

这儿其实有个问题:前面刚说了 Nothing 不会有任何的实例,对吧?那么这个右边就算能填 Nothing 类型的对象,可是这个对象我用谁啊?谁也没法用。

但是如果不直接用 Nothing,而是把它作为泛型类型的实例化参数。一个元素类型为Nothing 的 List,将会导致我无法找到任何的元素实例来填充进去,但是这个 emptyList 本身是可以被创建的:

val emptyList: List<Nothing> = listOf()
var apples: List<Apple> = emptyList

结合上我们刚说的「Nothing 是所有类型的子类型」这个特性,我们是不是可以把这个空的 List 赋值给任何的 List 变量?(这里实际上用到了协变的知识)

val emptyList: List<Nothing> = listOf()
var apples: List<Apple> = emptyList
var users: List<User> = emptyList
var phones: List<Phone> = emptyList
var images: List<Image> = emptyList

这样,是不是就提供了一个通用的空 List 出来,让这一个对象可以用于所有 List 的初始化。有什么好处?既省事,又省内存,这就是好处。

这种用法不只可以用在 List,Set 和 Map 也都没问题:

val emptySet: Set<Nothing> = setOf()
var apples: Set<Apple> = emptySet
var users: Set<User> = emptySet
var phones: Set<Phone> = emptySet
var images: Set<Image> = emptySet
val emptyMap: Map<String, Nothing> = emptyMap()
var apples: Map<String, Apple> = emptyMap
var users: Map<String, User> = emptyMap
var phones: Map<String, Phone> = emptyMap
var images: Map<String, Image> = emptyMap

而且也不限于集合类型,只要是泛型都可以,你自定义的也行:

val emptyProducer: Producer<Nothing> = Producer()
var appleProducer: Producer<Apple> = emptyProducer
var userProducer: Producer<User> = emptyProducer
var phoneProducer: Producer<Phone> = emptyProducer
var imageProducer: Producer<Image> = emptyProducer

它的核心在于,你利用 Nothing 可以创建出一个通用的「空白」对象,它什么实质内容也没有,什么实质工作也做不了,但可以用来作为泛型变量的一个通用的空白占位值。

4.作用三:语法的完整化

另外,Nothing 的「是所有类型的子类型」这个特点,还帮助了 Kotlin 语法的完整化。

在 Kotlin 的下层逻辑里,throw 这个关键字是有返回值的,它的返回值类型就是 Nothing。虽然说由于抛异常这件事已经跳出了程序的正常逻辑,所以 throw 返回不返回值、返回值类型是不是 Nothing 对于它本身都不重要,但它让这种写法成为了合法的:

val nothing: Nothing = throw RuntimeException("抛异常!")

并且因为 Nothing 是所有类型的子类型,所以我们这么写也行:

val nothing: String = throw RuntimeException("抛异常!")

看起来没用是吧?如果我再把它改改,就有用了:

var _name: String? = null
val name: String = _name ?: throw NullPointerException("_name 在运行时不能为空!")

throw 的返回值是 Nothing,我们就可以把它写在等号的右边,在语法层面假装成一个值来使用,但其实目的是在例外情况时抛异常。虽然 throw 不会真正地返回,但这让语法层面变得完全说得通了,这也是 Nothing 的价值所在。

除了 throw 之外,单独的 return 也是被规定为返回 Nothing 的一个关键字,所以我也可以这么写:

fun sayMyName(first: String, second: String) {val name = if (first == "Walter" && second == "White") {"Heisenberg"} else {return // 语法层面的返回值类型为 Nothing,赋值给 name}println(name)
}

单独 return 语法层面的返回值类型为 Nothing,可以赋值给 name,让代码从语法层面就能得到解释,从而帮助了 Kotlin 语法的完整化。


参考文章:
这玩意真的有用吗?对,是的!Kotlin 的 Nothing 详解

相关文章:

解析Kotlin中的Nothing【笔记摘要】

1.Nothing的本质 Nothing 的源码很简单&#xff1a; public class Nothing private constructor()可以看到它是个class&#xff0c;但它的构造函数是 private 的&#xff0c;这就导致我们没法创建它的实例&#xff0c;并且在源码里 Kotlin 也没有帮我们创建它的实例。 基于这…...

toRefs 和 toRef

文章目录 toRefs 和 toReftoRefstoRef toRefs 和 toRef toRefs toRefs 把一个由reactive对象的值变为一个一个ref的响应式的值 import { ref, reactive, toRefs, toRef } from vue; let person reactive({name: 张三,age: 18, }); // toRefs 把一个由reactive对象的值变为一…...

Vision Transformer论文阅读笔记

目录 An Image is Worth 16x16 Words: Transformers for Image Recognition at Scale -- Vision Transformer摘要Introduction—简介RELATED WORK—相关工作METHOD—方法VISION TRANSFORMER (VIT)—视觉Transformer(ViT) 分析与评估PRE-TRAINING DATA REQUIREMENTS—预训练数据…...

MapReduce的执行流程排序

MapReduce 是一种用于处理大规模数据集的分布式计算模型。它将作业分成多个阶段&#xff0c;以并行处理和分布式存储的方式来提高计算效率。以下是 MapReduce 的执行流程以及各个阶段的详细解释&#xff1a; 1. 作业提交&#xff08;Job Submission&#xff09; 用户通过客户端…...

雅思词汇及发音积累 2024.7.3

银行 check &#xff08;美&#xff09;支票 cheque /tʃek/ &#xff08;英&#xff09;支票 ATM 自动取款机 cashier 收银员 teller /ˈtelə(r)/ &#xff08;银行&#xff09;出纳员 loan 贷款 draw/withdraw money 提款 pin number/passsword/code …...

Vue2和Vue3的区别Vue3的组合式API

一、Vue2和Vue3的区别 1、创建方式的不同&#xff1a; &#xff08;1&#xff09;、vue2:是一个构造函数&#xff0c;通过该构造函数创建一个Vue实例 new Vue({})&#xff08;2&#xff09;、Vue3:是一个对象。并通过该对象的createApp()方法&#xff0c;创建一个vue实例。 Vue…...

ML307R OpenCPU HTTP使用

一、函数介绍 二、示例代码 三、代码下载地址 一、函数介绍 具体函数可以参考cm_http.h文件,这里给出几个我用到的函数 1、创建客户端实例 /*** @brief 创建客户端实例** @param [in] url 服务器地址(服务器地址url需要填写完整,例如(服务器url仅为格式示…...

【状态估计】线性高斯系统的状态估计——离散时间的递归滤波

前两篇文章介绍了离散时间的批量估计、离散时间的递归平滑&#xff0c;本文着重介绍离散时间的递归滤波。 前两篇位置&#xff1a;【状态估计】线性高斯系统的状态估计——离散时间的批量估计、【状态估计】线性高斯系统的状态估计——离散时间的递归平滑。 离散时间的递归滤波…...

架构设计上中的master三种架构,单节点,主从节点,多节点分析

文章目录 背景单节点优点缺点 主从节点优点缺点 多节点优点缺点 多节点&#xff0c;多backup设计优点缺点 总结 背景 在很多分布式系统里会有master,work这种结构。 master 节点负责管理资源&#xff0c;分发任务。下面着重讨论下master 数量不同带来的影响 单节点 优点 1.设…...

如何在 SQL 中删除一条记录?

如何在 SQL 中删除一条记录&#xff1f; 在 SQL 中&#xff0c;您可以使用DELETE查询和WHERE子句删除表中的一条记录。在本文中&#xff0c;我将向您介绍如何使用DELETE查询和WHERE子句删除记录。我还将向您展示如何一次从表中删除多条记录 如何在 SQL 中使用 DELETE 这是使…...

JavaSE (Java基础):面向对象(上)

8 面向对象 面向对象编程的本质就是&#xff1a;以类的方法组织代码&#xff0c;以对象的组织&#xff08;封装&#xff09;数据。 8.1 方法的回顾 package com.oop.demo01;// Demo01 类 public class Demo01 {// main方法public static void main(String[] args) {int c 10…...

flink使用StatementSet降低资源浪费

背景 项目中有很多ods层&#xff08;mysql 通过cannal&#xff09;kafka&#xff0c;需要对这些ods kakfa做一些etl操作后写入下一层的kafka&#xff08;dwd层&#xff09;。 一开始采用的是executeSql方式来执行每个ods→dwd层操作&#xff0c;即类似&#xff1a; def main(…...

FineDataLink4.1.9支持Kettle调用

FDL更新至4.1.9后&#xff0c;新增kettle调用功能&#xff0c;支持不增加额外负担的情况下&#xff0c;将现有的Kettle任务平滑迁移到FineDataLink。 一、更新版本前存在的问题与痛点 在此次功能更新前&#xff0c;用户可能会遇到以下问题&#xff1a; 1.对于仅使用kettle的…...

SwanLinkOS首批实现与HarmonyOS NEXT互联互通,软通动力子公司鸿湖万联助力鸿蒙生态统一互联

在刚刚落下帷幕的华为开发者大会2024上&#xff0c;伴随全场景智能操作系统HarmonyOS Next的盛大发布&#xff0c;作为基于OpenHarmony的同根同源系统生态&#xff0c;软通动力子公司鸿湖万联全域智能操作系统SwanLinkOS首批实现与HarmonyOS NEXT互联互通&#xff0c;率先攻克基…...

Win11禁止右键菜单折叠的方法

背景 在使用windows11的时候&#xff0c;会发现默认情况下&#xff0c;右键菜单折叠了。以至于在使用一些软件的右键菜单时总是要点击“显示更多选项”菜单展开所有菜单&#xff0c;然后再点击。而且每次在显示菜单时先是全部展示&#xff0c;再隐藏一下&#xff0c;看着着实难…...

Maven列出所有的依赖树

在 IntelliJ IDEA 中&#xff0c;你可以使用 Maven 插件来列出项目的依赖树。Maven 插件提供了一个名为dependency:tree的目标&#xff0c;可以帮助你获取项目的依赖树详细信息。 要列出项目的依赖树&#xff0c;可以执行以下步骤&#xff1a; 打开 IntelliJ IDEA&#xff0c;…...

测试开发面试题和答案

Python 请解释Python中的列表推导式&#xff08;List Comprehension&#xff09;是什么&#xff0c;并给出一个示例。 答案&#xff1a; 列表推导式是Python中一种简洁的构建列表的方法。它允许从一个已存在的列表创建新列表&#xff0c;同时应用一个表达式来修改或选择元素。…...

llm学习-3(向量数据库的使用)

1&#xff1a;数据读取和加载 接着上面的常规操作 加载环境变量---》获取所有路径---》加载文档---》切分文档 代码如下&#xff1a; import os from dotenv import load_dotenv, find_dotenvload_dotenv(find_dotenv()) # 获取folder_path下所有文件路径&#xff0c;储存在…...

【01-02】Mybatis的配置文件与基于XML的使用

1、引入日志 在这里我们引入SLF4J的日志门面&#xff0c;使用logback的具体日志实现&#xff1b;引入相关依赖&#xff1a; <!--日志的依赖--><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-api</artifactId><version&g…...

Linux-进程间通信(IPC)

进程间通信&#xff08;IPC&#xff09;介绍 进程间通信&#xff08;IPC&#xff0c;InterProcess Communication&#xff09;是指在不同的进程之间传播或交换信息。IPC 的方式包括管道&#xff08;无名管道和命名管道&#xff09;、消息队列、信号量、共享内存、Socket、Stre…...

【根据当天日期输出明天的日期(需对闰年做判定)。】2022-5-15

缘由根据当天日期输出明天的日期(需对闰年做判定)。日期类型结构体如下&#xff1a; struct data{ int year; int month; int day;};-编程语言-CSDN问答 struct mdata{ int year; int month; int day; }mdata; int 天数(int year, int month) {switch (month){case 1: case 3:…...

【JavaEE】-- HTTP

1. HTTP是什么&#xff1f; HTTP&#xff08;全称为"超文本传输协议"&#xff09;是一种应用非常广泛的应用层协议&#xff0c;HTTP是基于TCP协议的一种应用层协议。 应用层协议&#xff1a;是计算机网络协议栈中最高层的协议&#xff0c;它定义了运行在不同主机上…...

三维GIS开发cesium智慧地铁教程(5)Cesium相机控制

一、环境搭建 <script src"../cesium1.99/Build/Cesium/Cesium.js"></script> <link rel"stylesheet" href"../cesium1.99/Build/Cesium/Widgets/widgets.css"> 关键配置点&#xff1a; 路径验证&#xff1a;确保相对路径.…...

Java如何权衡是使用无序的数组还是有序的数组

在 Java 中,选择有序数组还是无序数组取决于具体场景的性能需求与操作特点。以下是关键权衡因素及决策指南: ⚖️ 核心权衡维度 维度有序数组无序数组查询性能二分查找 O(log n) ✅线性扫描 O(n) ❌插入/删除需移位维护顺序 O(n) ❌直接操作尾部 O(1) ✅内存开销与无序数组相…...

如何将联系人从 iPhone 转移到 Android

从 iPhone 换到 Android 手机时&#xff0c;你可能需要保留重要的数据&#xff0c;例如通讯录。好在&#xff0c;将通讯录从 iPhone 转移到 Android 手机非常简单&#xff0c;你可以从本文中学习 6 种可靠的方法&#xff0c;确保随时保持连接&#xff0c;不错过任何信息。 第 1…...

OpenLayers 分屏对比(地图联动)

注&#xff1a;当前使用的是 ol 5.3.0 版本&#xff0c;天地图使用的key请到天地图官网申请&#xff0c;并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能&#xff0c;和卷帘图层不一样的是&#xff0c;分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...

OPenCV CUDA模块图像处理-----对图像执行 均值漂移滤波(Mean Shift Filtering)函数meanShiftFiltering()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 在 GPU 上对图像执行 均值漂移滤波&#xff08;Mean Shift Filtering&#xff09;&#xff0c;用于图像分割或平滑处理。 该函数将输入图像中的…...

Springboot社区养老保险系统小程序

一、前言 随着我国经济迅速发展&#xff0c;人们对手机的需求越来越大&#xff0c;各种手机软件也都在被广泛应用&#xff0c;但是对于手机进行数据信息管理&#xff0c;对于手机的各种软件也是备受用户的喜爱&#xff0c;社区养老保险系统小程序被用户普遍使用&#xff0c;为方…...

pycharm 设置环境出错

pycharm 设置环境出错 pycharm 新建项目&#xff0c;设置虚拟环境&#xff0c;出错 pycharm 出错 Cannot open Local Failed to start [powershell.exe, -NoExit, -ExecutionPolicy, Bypass, -File, C:\Program Files\JetBrains\PyCharm 2024.1.3\plugins\terminal\shell-int…...

DeepSeek源码深度解析 × 华为仓颉语言编程精粹——从MoE架构到全场景开发生态

前言 在人工智能技术飞速发展的今天&#xff0c;深度学习与大模型技术已成为推动行业变革的核心驱动力&#xff0c;而高效、灵活的开发工具与编程语言则为技术创新提供了重要支撑。本书以两大前沿技术领域为核心&#xff0c;系统性地呈现了两部深度技术著作的精华&#xff1a;…...