Kotlin 初体验: 用 Kotlin 写命令行工具

导语 :可喜可贺, kotlin 在今年的 google I/O 大会上, 成为 google android 平台的新一门官方语言, 偶尔有了个写工具的机会试着用它来替代原来常用的 python

工具需求

工具需求很简单: 批量处理 proj 工程代码, 对符合条件的代码做后续的字符串替换, 然后存储到目标路径

按理说用批处理加 find/sed 工具也能搞定, python 撸脚本工具也一样高效, 但我想体验一下 kotlin, 所以就用它上了, 结果一晚上, 百行代码解决问题

工具执行大概示例如图

如图, root 下边 N 层目录, M 个文件 找到关心的文件(示例中为 *.java ) 根据规则替换文件内容, 重新把替换内容写入目标位置

文件处理流程

如上图画了个大致图示, 因为过滤文件规则, 按照规则匹配和替换, 这两个操作不依赖额外的资源, 可以进行一定的多线程并行

了解 reactive programming 的同学应该会感到这个图示相当山寨, 和那种 marble 图挺像, 确实如此.

so, 就是用reactive.io对应的kotlin库, 实现这套功能

代码流水账

1. 线程池初始化

获取机器核数, 自定义线程池: 自定义线程池代码, 因为要指定线程名称, 所以实现了匿名 ThreadFactory 实现类, kotlin 代码相对比较简单

2. 参数解析

命令行工具当然需要读入参数了 我这里定义 src, dst 参数 使用 joptsimple.OptionParser 轻松实现:

3. 解析参数, 根据目标文件夹, 创建目录结构

工具中如果指定了 dst, 那么会镜像创建 src 的所有文件夹, 实现如下

kotlin 中的类型转换用 as 这个关键字, 其中 as String, 表示会将结果转换为 String, 如果结果为 null, 那么这里直接会发生运行时异常 第二句, 先转换成 String? 类型, 即可为空的String类型, 接下来 ?: srcPath, 表示如果为null, 那么使用 srcPath 后边的判断, 如果srcPath和dstPath不等, 那么按照srcPath创建目录结构 这里String的判等, 用两个=号, 判断值类相等, 用三个=号, 判断引用相等 kotlin的文件遍历有一个函数式的扩展: walk(), 用起来也是函数式的感觉, 一气呵成

4. 给reactive库创建几个线程切换的scheduler

可以看到 readScheduler/writeScheduler, ThreadFactory 没有显式的写匿名对象, 因为 kotlin 支持这种 lambda 式的语法糖, 来实现一个单函数接口(比如 Runnable / Callable), 后边还会看到

5. 执行图示的那一坨 reactive 流程

完成, 就是这么简单

rxkotlin的扩展, 简化了各种常用类型创建Observable对象的写法:

多线程过滤部分:

可以看到 filter和flatmap 里边又是一个 lambda 实现了一个接口 (java8中也是类似的, x -> {})

读取文件部分:

读取文件这里之所以用flatMap, 是因为需要每次读完文件后, 下一步切换到多线程 scheduler

写文件部分:

kotlin没有 java 的 X ? y : z 的三目运算语法, 只能写成if else, 有点类似python.

计数统计部分

count().blockingGet()返回的便是最后写了多少个文件的个数了.

其他:

替换的实现在哪里呀?

其实就在这里

就是这一句 .compose(processStrategy) processStrategy是一个由你实现的 ObservableTransformer , 随意发挥即可 比如这个实现:

在文本中找TODO的注释, 然后替换成””, 不想再骗自己了, 注意到这里用flatMap顺带做了filter的效果, 如果没有找到匹配, 相当于就在这一步被过滤掉, 不会进行下一步写文件.

写完了怎么编jar包?

我这里使用的是gradle + kotlin插件, 可以继承一个jar的任务来生成fatjar, 我的整个build.gradle如下:

我看kotlin

kotlin 值得一玩了, 如果用在生产环境, 也需要团队的统一规范, 以及琢磨一下最好的实践. 自己并没有深入学习 kotlin, 只是了解了些皮毛, 会写像 C 一样的 Java, 会写像 Java 一样的 Kotlin…

看到有同事写的 < 为什么我不喜欢Kotlin > : http://km.oa.com/group/18297/articles/show/305773, 说到了代码复杂度的增加, 理解的困难, 说的没错, 毕竟工具是死的人是活的.

因为没有深入理解, 也没有搞过 kotlin 的大型项目, 不敢吹捧:), 但这次的初体验, 总的感觉还不错.

喜欢的点

  1. 常量特性 val, 对 @NonNull 这种修饰的原生支持
  2. 对 Nullable 的 fallback 语法
  3. 字符串模板
  4. data class
  5. apply, let, with …
  6. 待补充

不喜欢的点

  1. X..Y 这样的 range 表示有点蛋疼. 因为定式思维, X..Y 的第一直觉会让我觉得是 [X, Y) 这样的开闭区间.
  2. class 里边没法放 static field, 查了一下似乎要写一个 Companion object, kotlin 的 singleton 模式难道也要这么写? -> 其实可以直接写 object xx {} 但也需要适应下.. 反直觉
  3. 待补充… 也许要多用才会躺坑, 发现新的点 :)

参考资料:

Introduction to Kotlin (Google I/O ‘17) - https://www.youtube.com/watch?v=X1RVYt2QKQE

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

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏葡萄城控件技术团队

七天学会ASP.NET MVC (三)——ASP.Net MVC 数据处理

? 第三天我们将学习Asp.Net中数据处理功能,了解数据访问层,EF,以及EF中常用的代码实现方式,创建数据访问层和数据入口,处理Post数据,以及数据验...

20610
来自专栏程序员的SOD蜜

使用8位字节的编码格式将字节流安全的转换成String

我们常用的编码格式有ASCII,Unicode,UTF-8,GB2312等,如何在这些编码之间安全转换呢? 最近做邮件系统,采用了OpenPOP组件,这是老外写...

1927
来自专栏技术博客

Json和Jsonp

  JSON和JSONP虽然只有一个字母的差别,但其实他们根本不是一回事儿:JSON是一种数据交换格式,而JSONP是一种依靠开发人员的聪明才智创造出的一种非官...

682
来自专栏一个会写诗的程序员的博客

TypeScript 之父简介:TS Anders Hejlsberg: Introducing TypeScript参考资料TypeScript入门指南(JavaScript的超集)

https://channel9.msdn.com/posts/Anders-Hejlsberg-Introducing-TypeScript

792
来自专栏技术分享

log4net 自定义Layout日志字段

最近在使用log4net的时候有一个简单的需求,就是自定义个格式化输出符。这个输出符是专门用来帮我记录下业务ID、业务类型的。比如,“businessID:32...

620
来自专栏Golang语言社区

厚土Go学习笔记 | 35. web服务器实现动态路径

有时候,我们希望给不同的用户有不同的服务路径。抛开那些高大上和深层次的原因,最浅显的,可以让用户觉得自己挺受重视。路径名称最好就是用户有关的字符串。我们这次以用...

2975
来自专栏Golang语言社区

厚土Go学习笔记 | 35. web服务器实现动态路径

有时候,我们希望给不同的用户有不同的服务路径。抛开那些高大上和深层次的原因,最浅显的,可以让用户觉得自己挺受重视。路径名称最好就是用户有关的字符串。我们这次以用...

26910
来自专栏极客慕白的成长之路

jQuery深入——动画、常用工具、JSON、Ajax

4、停止动画 - stop([stopAll [, goToEnd]]) stopAll 布尔值,规定是否停止被选元素的所有加入队列的动画。默认是 false。...

1001
来自专栏美码师

补习系列-springboot mime类型处理

MIME的全称是Multipurpose Internet Mail Extensions,即多用途互联网邮件扩展,尽管读起来有些拗口,但大多数人可能都知道, ...

932
来自专栏Web项目聚集地

从零学习Spring MVC框架「RESTful风格实践」

学习本文章之前,我们需要知道什么是RESTful API,还对此不了解的朋友可以移步历史文章 RESTful 接口实现简明指南 ,简单来说就是就是用URL定位资...

801

扫码关注云+社区