Kotlin作用域函数引发的遮蔽问题

ops/2025/1/15 21:51:06/

前面讲了kotlinit变量引起的遮蔽问题,见Kotlin it隐式变量的遮蔽问题,本篇聊聊作用域函数(scoped function)可能引起的遮蔽问题。

先来看一个简单的示例:

kotlin">fun test(): String {val s: String = "asdf".also {println(it.length)}return s
}

这里涉及3个上下文:全局上下文、test函数局部上下文、also引入的上下文。

然后第2个示例:

kotlin">fun test(): String {val s: String = "asdf".apply {println(length)}return s
}

不过这里涉及4个上下文:全局上下文、test函数局部上下文、apply引入的大括号之间的上下文,以及调用apply的字符串对象的对象上下文。

两段程序极为相似,在词法和语法结构上光靠肉眼难以发现差异,但上下文环境却有着较大的差别。在我第一次学习到Kotlin的这个特性时,心里有些不安。

传统的函数式语言中,如Lisp,在使用类似Lambda表达式之类的匿名函数时会引入新的上下文,Java也同理,但不会引入新的对象上下文(Java要同时引入新的函数和对象上下文需要用匿名内部类)。而局部匿名函数在Kotlin中也会引入这个新的函数上下文,却可能引入一个隐式的对象上下文。而这个对象上下文能否观察到、是否醒目就取决于编辑器的提示了。

Kotlin中这样的设计可以让代码变得简洁,但有点过于简洁了,省略了一些有助于阅读的信息,在发生遮蔽的时候有些代码可能反而没有那么直观,比如:

kotlin">fun test(): String {val length = 1val s: String = "asdf".apply {println(length) // length ?}return s
}

kotlin">class Test {val length = 1fun test(): String {val s: String = "asdf".apply {println(length) // length ?}return s}
}

大家可以看出println(length)中的length分别指向哪个对象吗?

所以,我认为作用域函数中引入对象的上下文,有利于简化代码的编写,但是因为其可能导致不易察觉的隐式遮蔽,所以对阅读可能造成影响。甚至,如果经常复制粘贴代码或编写长篇大段的代码,在功能迭代更新时可能因为没有注意到遮蔽的发生而导致bug。

一点点建议

  1. 在使用作用域函数(scoped function)时尽量避免多层嵌套,尤其是多个作用域函数嵌套。(一种较为消极且安全的方式)
  2. 在引入对象上下文的作用域函数(例如apply)中使用this来引用对象的成员,来减少遮蔽想象的发生,方便阅读,例如:
kotlin">fun test(): String {val length = 1val s: String = "asdf".apply {println(this.length)}return s
}
  1. 避免不同上下文中同名变量的使用。(包括开发者定义的变量和隐式的it
  2. 标准库中的能满足或普通函数就能满足,尽量避免自定义一些作用域函数
  3. 团队中如果不可避免地自定义/扩展了作用域函数,就保持api的稳定,并完善相关文档说明和规范(比如,“新的自定义作用域函数禁止引入对象上下文”或对名称进行规范)

http://www.ppmy.cn/ops/16517.html

相关文章

《前端面试题》- React - 如何区分函数组件和类组件

问题 如何区分函数组件和类组件? 答案 可以使用instanceof 或者Component.prototype.isReactComponent。 示例 函数组件 export default function FunctionComonent() {if(FunctionComonent.prototype.isReactComponent){console.log(FunctionComonent是类组件…

GRE技术原理:揭开网络封装的神秘面纱

GRE技术原理:揭开网络封装的神秘面纱 概述 本文将深入探讨GRE(Generic Routing Encapsulation,通用路由封装)技术的原理。我们将从GRE的基本概念出发,分析其在网络中的应用场景,详细阐述其封装与解封装过…

推荐一个带java环境的tomcat镜像,使用jdk 1.8.0_312

镜像名称和版本:tomcat:10.0-jdk8拉取命令:docker pull tomcat:10.0-jdk8镜像tar包(百度网盘): 链接:https://pan.baidu.com/s/1TM8k7Z97NYhahCrrTh3vBw?pwdrq93 提取码:rq93docker启动命令&am…

Golang实现一个批量自动化执行树莓派指令的软件(5)模块整合

简介 基于上篇 Golang实现一个批量自动化执行树莓派指令的软件(4)上传 已经实现了ssh的基本操作模块了,这里我们将这些模块进行整合。 环境描述 运行环境: Windows, 基于Golang, 暂时没有使用什么不可跨平台接口, 理论上支持Linux…

Cache缓存

在计算机架构中,缓存(Cache)是一种高速数据存储层,它存储了一部分从原始数据源(如主内存)频繁访问的数据副本。通过将数据暂时存储在物理上更接近处理器的位置,缓存能够减少数据访问的延迟&…

牛客NC199 字符串解码【中等 递归,栈的思想 C++/Java/Go/PHP】

题目 题目链接: https://www.nowcoder.com/practice/4e008fd863bb4681b54fb438bb859b92 相同题目: https://www.lintcode.com/problem/575 思路 解法和基础计算器1,2,3类似,递归参考答案C struct Info {string str;int stopindex;Info(str…

4.25日学习记录

[HZNUCTF 2023 preliminary]ppppop 对于php反序列化,在之前的学习中有过了解,但是对于序列化字符串的格式不是很了解,刚好接触这题,可以了解一下 序列化字符串的格式: 布尔型(bool)b&#xf…

计算机毕业设计Flask+Vue.js知识图谱音乐推荐系统 音乐爬虫可视化 音乐数据分析 大数据毕设 大数据毕业设计 机器学习 深度学习 人工智能

开发技术 协同过滤算法、机器学习、LSTM、vue.js、echarts、django、Python、MySQL 创新点 协同过滤推荐算法、爬虫、数据可视化、LSTM情感分析、短信、身份证识别 补充说明 适合大数据毕业设计、数据分析、爬虫类计算机毕业设计 介绍 音乐数据的爬取:爬取歌曲、歌手…