糟糕的 Kotlin 语法糖

简介: 糟糕的 Kotlin 语法糖

这几天在 review 同事的代码的时候,发现一块有意思的代码,我将其写成对应的伪代码如下:


class UserViewModel(val userUsecase: UserUsecase) {
    // 根据 userId 获取 userName
    fun getUser(userId:Int) {
        val name = userUsecase(userId).name
    }
}
class User(val name: String, val age: Int) {}
复制代码


起初在看到这段代码的时候,觉得十分反人类,在 Kotlin 中,对象的初始化可以省略 new 操作符,也即类后面再配个 () 即可,为啥一个初始化的对象还能继续用 (),在直观的感受下,我以为是初始化了一个对象,唯一让我觉得不像是初始化的就是 userUsecase 开头并不是大写,这才打消我认为他是初始化对象的疑虑。


在我想点进去看下根据 userId 获取 User 的过程,我无论追踪代码,都无法跳转到真正的逻辑代码调用处,点击 userUsecase 会直接跳转到 UserViewModel 的构造方法,点击 name 会跳转到 User 对象,这让我很苦恼。


我不得不点击 UserUsecase 类去看下里面的代码,这对于 review 人来说简直是灾难,但为了解决问题,先妥协,再一探究竟。


进入 UserUsecase 类,伪代码如下:


class UserUsecase {
    operator fun invoke(userId: Int): User {
        // 从数据库中根据 id 获取 User 数据
        // 返回 User 数据
        return User("lisi", 30)
    }
}
复制代码


看到了奇怪的 invoke 函数,并且是用了 operator 操作重载符,为了了解这种语法,我在 Kotlin 中文网查了下该语法的使用,在调用操作符章节中有所说明:

image.png

对象() 等价于 对象.invoke()()内为函数的参数,也即我们上面的那段代码,可以翻译一下:


class UserViewModel(val userUsecase: UserUsecase) {
    fun getUser() {
        val name = userUsecase(1001).name
        // 等价于
        val name2 = userUsecase.invoke(1001).name
    }
}
复制代码


也可以用 Kotlin Decompile 看下结果:


image.png

需要说明的是,对象() 这种写法是有条件的:


  • 必须用 operator 修饰方法
  • 方法名称必须是 invoke
  • invoke 参数可以多个,不做限制


由于 invoke 函数参数不加限制,这又带来了一个问题,如果重载了多个 invoke 函数,就更不知道业务方在调用的时候是做了什么事情,依然不得不进入代码才能知道逻辑。

上面的示例给的已足够简单,但实际在我们的业务中,比这还复杂,invoke 函数被封装到了父类,当我点进去的时候根本找不到 invoke 函数,只能往上查看父类有没有,在找到 invoke 函数时才发现,他最终调用了个抽象方法,该抽象方法由子类实现,我又不得不返回到子类查看这个方法,最终才敲定这个方法做了什么逻辑。


总结:


虽然 operator invoke 可以省略调用方写函数名这个过程,但需要注意的是,代码无论是类名还是方法名还是变量名,一定要做到见名识意,显然,他已经破坏了这个规则,让 review 人很抓狂。


我也很理解大家对 Jetpack 的热爱,这种写法在官方也有出现,可以参考 Domain Layer 这章。但我想说的是,省略方法名这个过程真的有必要吗?写代码到底是为了炫技还是为了让别人能看懂自己的代码呢?

目录
相关文章
|
运维 监控 数据库
如何实现软件SaaS化
如何实现软件SaaS化
|
3月前
|
Windows 自然语言处理
Ollama Modelfile 详细使用手册
想用Ollama打造专属模型?Modelfile就是你的“模型食谱”!本文以做菜为喻,零基础手把手教你写Modelfile:FROM选基模、PARAMETER调温度/记忆、SYSTEM定角色(如马里奥)、TEMPLATE规范格式、MESSAGE给示例。全程无术语,附实操步骤与避坑指南,看完即能创建并运行自己的第一个自定义模型。
|
11月前
|
传感器 人工智能 算法
聚焦“以技术集成支撑单亩价值创造”与“增加值分配机制区块链存证确权”两大核心本质
“振兴链-技术集成科技小院”以技术集成与区块链为核心,推动农业现代化。通过多维度技术整合(如精准农业、物联网等),突破资源约束,最大化单亩产值;同时利用区块链确权存证,建立透明分配机制,解决传统农业中收益不均问题。技术赋能生产,制度重塑分配,实现效率与公平的平衡,助力乡村振兴与产业升级。典型场景显示,该模式可显著提升单亩价值并确保增值公平分配。
|
5月前
|
NoSQL 物联网 数据管理
阿里云数据库MongoDB版收费价格:分片集群和副本集费用清单
阿里云MongoDB版提供副本集与分片集群,兼容MongoDB协议,支持文档型数据管理。2核8GB独享型首月免费,新用户享699元/3个月或1999元/年等优惠,全系低至6折/年,适用于移动、物联网等场景。
536 9
|
监控 关系型数据库 MySQL
初体验:数据库监控、管理和可观测性工具(PMM)
Percona Monitoring and Management (PMM) 是一个开源工具,用于监控MySQL、PostgreSQL和MongoDB的性能。它提供实时监控、数据可视化、故障排除和管理功能,支持本地和云端数据库。要安装PMM,首先需安装Docker,然后通过提供的脚本部署PMM服务器和客户端。在MySQL服务器上创建PMM用户后,使用`pmm-admin`命令添加数据库。访问PMM的HTTPS网址(默认用户名和密码为admin)进行配置。本文还包含了安装Docker和PMM的命令行步骤。
初体验:数据库监控、管理和可观测性工具(PMM)
|
存储 缓存 监控
多级缓存有哪些级别?
【10月更文挑战第24天】多级缓存有哪些级别?
366 1
|
监控 Linux 定位技术
Linux应用开发基础知识——串口应用编程(十一)
Linux应用开发基础知识——串口应用编程(十一)
762 0
Linux应用开发基础知识——串口应用编程(十一)
|
弹性计算 算法 Java
一文说清linux system load averages
深入浅出阐释linux system load averages的语义,算法和计算流程,并分享了实际load飙高问题的排查经验和心得。
一文说清linux system load averages
|
消息中间件 运维 监控
使用 Kafka面临的挑战
本文详细探讨了Apache Kafka在实际部署与使用过程中可能遇到的各种挑战,包括集群配置、性能调优、数据一致性及安全性等方面的问题。尤其针对中小型企业,提出应充分利用云服务来避免自行搭建Kafka集群所带来的复杂运维工作。通过深入分析这些问题,旨在帮助企业更好地利用Kafka的优势,同时确保系统的稳定与高效运行。
356 0
|
SQL 监控 安全
Java Web应用的安全防护与攻防策略
Java Web应用的安全防护与攻防策略