-
Notifications
You must be signed in to change notification settings - Fork 541
Closed
Labels
Milestone
Description
问题和需求描述
在一次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)还高。
作为一个写kotlin各种平台(android/后台/multiplatform)代码,并且设计过kotlin框架的人,我认为这些函数存在以下问题:
- 这些扩展函数没有作用域,他们在项目的任何地方都可以调用,而不是在某些特定的类内
- 这些函数为了达到使用简单,和系统提供的很多常用的函数重名,如contains,to(kotlin使用to作为二维元组的连接中缀,相当于JS里面的“:”,不过好在一般会使用"foo" to "bar",很少会使用"foo".to("bar"))等,函数名和参数里也没有体现出和JSON有关的任何标识
- 这些扩展函数的使用对象为
Any
,如contains
,eval
,writeTo
等,很多类可能有自己的contains、eval、writeTo函数,在调用时很可能会调错
请描述你建议的实现方案
- 修改函数名
像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上的扩展函数是一种很不负责任的起名方式,毕竟扩展函数会对整个项目所有的对象函数调用进行污染。
- 另一种解决方法,增加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)