专栏首页Bennyhuo解毒 Kotlin Koans: 02 震惊!你的 Java 代码居然被转换成了这样...

解毒 Kotlin Koans: 02 震惊!你的 Java 代码居然被转换成了这样...

上回书我们说道,一个简单的 HelloWorld 背后也可以隐藏着众多不可告人的秘密。那么这些秘密究竟是什么呢?

那就是,只要我们写的代码可以支持下面的代码运行,并返回 "OK",那么这事儿就成啦:

 start() 

既然这样,我们除了可以有上一回提到的两种普通解法之外,还应该有以下几种高端解法:

  1. 默认参数法: fun start(str: String = "OK") = str
  2. Lambda 表达式/匿名函数法 val start = { "OK" }
  3. 运算符重载法 object start{ operator fun invoke() = "OK" }

你还想到什么有趣的解法了么?

1. 转换 Java 为 Kotlin

大家学习 Kotlin,一定知道有个神奇叫做 "Convert Java File to Kotlin File",不仅如此,如果你复制一段 Java 代码到 Kotlin 文件中,这段代码也会自动转换成 Kotlin 代码。

有很多时候如果你不知道某种东西怎么用 Kotlin 表达,怎么办呢?你总不能说:小二,给洒家来一本牛津大辞典吧?还好有 J2K 转换工具,这些问题有时候只要你会 Java,你就可以丧心病狂的转换出 Kotlin 代码。

我们今天按照 Kotlin Koan 给出的顺序,要解毒的就是下面这道题:

  • 把下面这段 Java 代码转换为 Kotlin 代码: public class JavaCode { public String toJSON(Collection<Integer> collection) { StringBuilder sb = new StringBuilder(); sb.append("["); Iterator<Integer> iterator = collection.iterator(); while (iterator.hasNext()) { Integer element = iterator.next(); sb.append(element); if (iterator.hasNext()) { sb.append(", "); } } sb.append("]"); return sb.toString(); } }

嗯,老夫想了想,这有何难,复制粘贴谁不会,真是的。

从此以后,我就成了 Kotlin 大神,反正只要用工具把 Java 代码转一下就好啦,还学什么学 >.<!

2. 什么玩意,空指针啊

后来我就经常需要将原来用 Java 编写的 Activity 转换为 Kotlin 版本的,例如:

 public class TestActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...        
    }
} 

老板说了,从今天起,谁写 Java 就是跟他作对(请允许我 YY 一下 - -、),于是没有办法,我就得把它转成 Kotlin:

 class TestActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle) {
        super.onCreate(savedInstanceState)
       ...
    }
} 

转的挺快啊,我还没反应过来,就转完了!不过这代码你要是敢运行一遍,Crash 就敢恶心你一遍。savedInstanceState 这个参数可能为 null,显然类型定为 Bundle 有些不合适。

对于平台类型(Platform Type),很多时候转换工具是无从得知它是否可能为空的,毕竟 Java 没有对此作出过任何承诺。

怎么办?Kotlin 提供了一对注解来标注 Java 类型是否可空:@Nullable@NotNull,Android Support Annotations 这个包也提供了一对:@Nullable@NonNull,我们用这些注解标注一下 Java 类型,那么再做转换,工具就会根据你做的标注来转换代码。

 @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
} 
 override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
   ...
} 

一句话,用转换工具的时候一定要注意平台类型!

3. Raw 类型惨叫一声...

尽管我们知道这转换工具没办法有效识别平台类型的问题,不过,对于下面的情况,它支持起来可能就更有些尴尬了:

 public class BaseView <T extends BasePresenter>{
    T presenter;
}
...
public class BasePresenter<T extends BaseView> {
    T view;
} 

下面是转换后的结果:

 class BaseView<T : BasePresenter<*>> {
   var presenter: T? = null
}
class BasePresenter<T : BaseView<*>> {
   var view: T? = null
} 

看上去也没啥问题啊,为啥 IDE 就报错呢?因为我们要求 BaseView 当中的 T 类型是 BasePresenter 的子类,不过我们对这里的 BasePresenter 有个小小的要求,那就是它的泛型参数得是 BaseView 的子类而不是 *。对于 BasePresenter 也是一样的。

那么 Java 中为什么没有这样的问题呢?因为 Java 中有 Raw 类型,你可以不传任何泛型参数给 BaseView 就像我们在声明 BasePresenter 的时候那样。

显然,对于 Raw 类型的转换,转换工具会用 * 来代替,但这样的代码有时候可以,有时候却是行不通的。

小心 Raw 类型!

传送门:Kotlin 泛型

4. Kotlin 风格的代码

吐槽转换工具就好比我们吐槽谷歌翻译一样:有时候不对,就像我们在 2、3 两节举的例子一样,

有时候呢,虽然不算错,但也实在是别扭...

比如我们今天提到的 Koans 的这道题目,代码转换的结果虽然是对的,但这代码直接暴露了你不会 Kotlin 的事实。会写 Kotlin 的人家都这么写:

 fun toJSON(collection: Collection<Int>): String = collection.joinToString(separator = ", ", prefix = "[", postfix = "]") 

不就是拼接字符串么,还用得着亲自动手?祭出 joinToString 神器,可以解决你日常 80% 的拼接需求了。

可是 Collection 怎么会有这么个方法,在 Java 里面没见过呀!于是你不相信这眼前的一切,点进去看了一下源码,就发现了扩展方法这样的大杀器,从此你的 Kotlin 装备增加了 100 点物理伤害,以及 40% 的攻速。

 public fun <T> Iterable<T>.joinToString(...): String {
    return joinTo(...).toString()
} 

掌握扩展方法不需要太多的前置条件,只要你有过迫切的想法想给 Date 添加一个 format 的方法的冲动,那么你就能理解这个特性是用来做什么的。

 fun Date.format(pattern: String): String {
    return SimpleDateFormat(pattern).format(this)
}
...
Date().format("yyyy-MM-dd HH:mm:ss") 

5. 本期问题

  1. 请大家阅读 Kotlin 泛型,并且给出第 3 节中提到的 BaseViewBasePresenter 的 Kotlin 的正确写法。
  2. 请大家为 String 添加扩展方法, 实现 "abc" - "bc" -> "a"

那么我们下周再见咯~

本文分享自微信公众号 - Kotlin(KotlinX),作者:bennyhuo

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2017-07-31

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 解毒 Kotlin Koans: 01 Introduction/HelloWorld

    Kotlin 火了吗?也许吧。反正以前不知道它的,现在陆陆续续知道了;以前不敢用它的,现在也开始慢慢接受了;以前就热衷于它的,比如我这样的(说着摸了摸自己的脸,...

    bennyhuo
  • 闲聊 Kotlin-Native (0) - 我们为什么应该关注一下 Kotlin Native?

    Kotlin-Native 的定位略显尴尬,为什么这么说呢?因为现在的编程语言实在太多了,新语言出来必然要解决现有某个语言的痛点,这样才能快速切入该语言所覆盖的...

    bennyhuo
  • Kotlin 反射有个坑你们知道么!

    话说有那么一天,想写个什么框架秀一秀 Kotlin 的反射,这个框架呢,需要获取一个类型的某一个方法,然后调用之。

    bennyhuo
  • 【翻译】17位谷歌专家称号大牛如何看待Kotlin

    2017-06-03 by Liuqingwen | Tags: Kotlin 翻译 | Hits

    IT自学不成才
  • resize失败原因调查

    对一个vm做resize,即从一个小的flavor换一个大的flavor,没有成功

    后端云
  • 掌握 Spring 之异常处理

    这次我们学习 Spring 的异常处理,作为一个 Spring 为基础框架的 Web 程序,如果不对程序中出现的异常进行适当的处理比如异常信息友好化,记录异常日...

    闻人的技术博客
  • lumen切换 nikic/fast-route 为 illuminate/routing

    接下来我们要替换路由组件。通过读取相关源码,我们知道注册路由组件是通过Laravel\Lumen\Application::bootstrapRouter()进...

    魔王卷子
  • MySQL 分库分表与分区的区别和思考

    说过很多次,不要拘泥于某一个技术的一点,技术是相通的。重要的是编程思想,思想是最重要的。当数据量大的时候,需要具有分的思想去细化粒度。当数据量太碎片的时候,需要...

    黄泽杰
  • 如何去除代码中的多次if而引发的一连串面试问题

    小白:不是,真正的工厂模式有两种:工厂方法和抽象工厂。工厂方法使用继承,首先定义一个抽象父类工厂,然后定义子类工厂,把工厂要创建的对象委托给子工厂类,子工厂类实...

    JavaQ
  • Java并发编程实战系列15之原子遍历与非阻塞同步机制(Atomic Variables and Non-blocking Synchronization)

    近年来,在并发算法领域的大多数研究都侧重于非阻塞算法,这种算法用底层的原子机器指令来代替锁来确保数据在并发访问中的一致性,非阻塞算法被广泛应用于OS和JVM中实...

    JavaEdge

扫码关注云+社区

领取腾讯云代金券