从 Kotlin 开发者的角度来看,Java 缺少了什么?( 二 )


②防止工具类实例化③static方法④一个简单的大写函数 , 没有考虑边界情况⑤String类型没有提供大写功能⑥使用工具类来实现该功能而Kotlin提供了扩展函数功能来解决这个问题 。
Kotlin提供了一种方法 , 可以扩展类或接口 , 而无需从类进行集成 , 也无需使用诸如修饰器等设计模式 。 只需通过一种叫做“扩展”的特殊定义来实现 。
例如 , 你可以给一个来自第三方库的类或接口编写新的函数 , 即使你无法修改该库 。 这种函数可以正常调用 , 就像它本来就属于该类一样 。 这种机制叫做“函数扩展” 。
要定义函数扩展 , 只需在其名称前加上一个接收者类型 , 指示被扩展的类 。
有了函数扩展 , 上述代码就可以写成:
funString.capitalize2():String{①②returnsubstring(0,1).uppercase()+substring(1);}valstring=randomString()valcapitalizedString=string.capitalize2()③①孤立的函数 , 不需要类封装 。
②Kotlin的stdlib已经有了capitalize()函数 。
③就像调用String自带的函数一样调用扩展函数 。
注意扩展函数会被“静态地”解析 。 扩展函数并不会给已有类型添加新的行为 , 只是假装而已 。 它们生成的字节码非常类似于Java的静态方法 。 但是其语法要简洁得多 , 而且支持函数链式调用 , 这在Java中时无法做到的 。
真实泛型Java版本5加入了泛型支持 。 但是 , 语言设计师太执着于向下兼容性 , Java5的字节码必须能与Java5之前的字节码完全兼容 。 这就是为什么生成的字节码中不包含泛型的原因 。 这种方式称为“泛型擦除” 。 与之相对的叫做“真实泛型”(reifiedgenerics) , 即泛型会出现在字节码中 。
仅在编译期间采用泛型 , 会导致一系列问题 。 例如 , 下面的方法签名会生成完全相同的字节码 , 因此这段代码是不正确的:
classBag{intcompute(Listpersons){}intcompute(Listpersons){}}另一个问题是如何从值的容器中获取有类型的值 。 下面是Spring中的一个例子:
org/springframework/beans/factory/BeanFactory.javapublicinterfaceBeanFactory{TgetBean(ClassrequiredType);}开发者添加了一个Class , 以便在方法体中获知类型 。 如果Java有真实泛型 , 只需像下面这样处理即可:
publicinterfaceBeanFactory{TgetBean();}想象一下 , 如果Kotlin有真实泛型 , 我们可以改变上述设计:
interfaceBeanFactory{fungetBean():T}函数调用可以改成:
valfactory=getBeanFactory()valanyBean=factory.getBean()①①真实泛型!
Kotlin依然需要遵守JVM规范 , 生成与Java编译器的字节码兼容的字节码 。 但它可以通过“内联”的方式实现 , 即编译器用函数体替换内联函数调用 。
下面是Kotlin代码:
org/springframework/beans/factory/BeanFactoryExtensions.ktinlinefun总结本文介绍了四个我希望Java也有的Kotlin功能:不可变引用、null安全性、扩展函数 , 以及真实泛型 。 Kotlin还有许多其他很好的功能 , 但这四个功能就足以提升Java 。
例如 , 有了扩展函数和真是繁星 , 再加上一些语法糖 , 就可以很轻松地编写DSL , 就像KotlinRoutes和BeansDSL一样:
beans{bean{router{GET("/hello"){ServerResponse.ok().body("Helloworld!")}}}}别误会:我知道Java作为一种语言 , 发展时需要考虑很多因素 , 而Kotlin的包袱更轻 。 但是 , 有竞争是好事 , 两者可以互相学习 。
同时 , 我只在必要时才会编写Java , 因为Kotlin已成为了我的JVM首选 。
原文地址:https://blog.frankel.ch/miss-in-java-kotlin-developer/