Skip to content

[FEATURE]修改kotlin扩展Any.contains函数名,以及其他类似的相关扩展函数 #1402

@ousc

Description

@ousc

问题和需求描述

在一次debug过程中,我们团队发现一句必定正确的逻辑判断永远返回false,最终我们发现是fastjson2间接引起的问题.

1.起因

fastjson2的kotlin扩展包为kotlin提供了许多扩展方法,如parseArray()parseObject()to<T>()into<T>()eval<T>()等,去年我们使用kotlin + springboot的项目将fastjson包升级到了2.0,升级的原因之一就是新版提供了kotlin支持,同时,我们引入kotlin扩展包的初期体验到了极大的方便:

  • 新的api和kotlin代码风格融合,在dsl语法的使用中简化了代码,提高了可读性。

但是,这些扩展函数的设计中,有少数几个扩展函数存在很大的问题。

拿我最开始的情况,举个例子:

kotlin.text包内String的contains函数是一个非常常用的函数,用于判断字符串的包含关系
自从2.0.4以来,fastjson2提供了一个没有限制作用域和类型的扩展函数Any.contains

@Suppress(
    "HasPlatformType",
    "NOTHING_TO_INLINE"
)
inline fun Any.contains(
    path: String
) = JSONPath.of(path).contains(this)

这个扩展函数经常会在调用String.contains(other)的时候被悄悄自动引入进来,导致一些非常隐含的报错,而且一旦引入,整个文件所有的contains都完了
如下图所示,它的优先级甚至比kotlin.text默认的contains(charSequence)还高。
截屏2023-04-24 11 05 47

作为一个写kotlin各种平台(android/后台/multiplatform)代码,并且设计过kotlin框架的人,我认为这些函数存在以下问题:

  1. 这些扩展函数没有作用域,他们在项目的任何地方都可以调用,而不是在某些特定的类内
  2. 这些函数为了达到使用简单,和系统提供的很多常用的函数重名,如contains,to(kotlin使用to作为二维元组的连接中缀,相当于JS里面的“:”,不过好在一般会使用"foo" to "bar",很少会使用"foo".to("bar"))等,函数名和参数里也没有体现出和JSON有关的任何标识
  3. 这些扩展函数的使用对象为Any,如containsevalwriteTo等,很多类可能有自己的contains、eval、writeTo函数,在调用时很可能会调错

请描述你建议的实现方案

  1. 修改函数名
    像Any.toJSONString这种就不会引起歧义,可以考虑修改函数名
    需要做的就是将有关的函数设置为废弃,修改函数名后重新发包(可以使用@deprecated + ReplaceWith)
@Deprecated(
    "The 'function xxx' xxx will be removed soon, please use xxxxx",
    ReplaceWith("xxx.xxx(xxx)", "xxxx")
)
fun xxx

我认为contains这个函数修改的优先级很高,其他名字中没有体现JSON的、加在Any上的扩展函数是一种很不负责任的起名方式,毕竟扩展函数会对整个项目所有的对象函数调用进行污染。

  1. 另一种解决方法,增加RequiresOptIn
    增加一个@RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "xxx")注解,在调用此类函数时需要额外加入一个Opt,及早发现调用了错误的函数,防止这一类bug的发生。
    例:
@RequiresOptIn(level = RequiresOptIn.Level.ERROR, message = "This is a function applied by fastjson2, please notice!")
@Retention(AnnotationRetention.BINARY)
@Target(AnnotationTarget.CLASS, AnnotationTarget.FUNCTION)
annotation class FastJsonForKotlin
@Suppress(
    "HasPlatformType",
    "NOTHING_TO_INLINE"
)
@FastJsonForKotlin
inline fun Any.contains(
    path: String
) = JSONPath.of(path).contains(this)

实际调用时:
截屏2023-04-24 12 04 57

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions