Tips1<使用kotlin语法糖让java代码更简洁>使用kotlin语法糖让java代码更简洁>
自从Kotlin1.3发布之后,协程脱离了实验环境成为了新特性其中的一员,但今天我们不讨论这个新的特性(对于JVM来说),而是关注那些自Kotlin1.0就存在并被使用的特性。
扩展方法是一个非常重要的特性。在Java中,当我们需要为一个类添加新的方法时,往往需要继承这个类并且码出这个方法,如果仅仅需要添加很少的方法(但将会很常用,有添加的必要),往往会选择写一个方法并将该类作为参数,以便实现一些操作。这样很麻烦,而且在这种工具方法比较多的时候管理起来非常混乱,所以一般还是会选择通过继承来完成。
Kotlin扩展函数允许我们直接向一个类中写入并使用这个方法,例如:需要一个方法,功能将一个字符串输出指定次数,在Java中:
void print(String s, int count) { for (int i = 0; i < count; i++) { System.out.println(s); }}而在Kotlin中,可以为String类直接添加一个方法:
fun String.print(count: Int) { for (i in 1..count) { print(this) }}这样就为String类新增了一个方法,调用此方法会变得非常方便:
val a: String = "no no no"a.print(2)"hello world".print(5)手头刚好有一段WebMagic代码,功能是抓取指定网页:
Spider.create(new AmazonListProcessor()) .
addUrl(WEB_SITE.LIST_PAGE_FORWARD+"?pg=1").
addUrl(WEB_SITE.LIST_PAGE_FORWARD+"?pg=2").
thread(5).
addPipeline(new AmazonListPipeline()) .
run();其中WEB_SITE.LIST_PAGE_FORWARD是一个静态字符串常量,是某网站的网页url,代码中用该url和参数拼出两个url并爬取,现在用扩展方法来实现:
fun String.runSpiderForUrl(processor: PageProcessor, thread: Int, pipeline: Pipeline) { Spider.create(processor) .addUrl(this) .thread(thread) .addPipeline(pipeline) .run()}这样调用就会变得非常简单:
(WEB_SITE.LIST_PAGE_FORWARD + "?pg=1").runSpiderForUrl(AmazonListProcessor(), 5, AmazonListPipeline())(WEB_SITE.LIST_PAGE_FORWARD + "?pg=2").runSpiderForUrl(AmazonListProcessor(), 5, AmazonListPipeline())基于这个方式,还可以将代码变得更简单,我们是将url手动拼接后使用的,而拼接这个过程也可以用扩展方法实现,流程为:将一个String类型的url拼接不同的参数,得到多个url,然后进行爬取。这里至少需要两个扩展方法:一个String的扩展方法,用于为目标String拼接多个参数/后缀,将结果保存在ArrayList中并返回;还有一个ArrayList
fun String.extend(vararg string: String): ArrayList<String> { val urlList = ArrayList<String>() for (s in string) { urlList.add(this + s) } return urlList}fun ArrayList<String>.runSpiderForUrl(processor: PageProcessor, thread: Int, pipeline: Pipeline) { for (string in this) { string.runSpiderForUrl(processor, thread, pipeline) }}这样,具体的爬取代码就可以改写为:
WEB_SITE.LIST_PAGE_FORWARD.extend("?pg=1", "?pg=2") .runSpiderForUrl(AmazonListProcessor(), 5, AmazonListPipeline())简洁又清爽。
Tips2<在kotlin中使用运算符重载>在kotlin中使用运算符重载>
Kotlin作为兼容Java的,提供了一个Java没有的特性:运算符重载;大家可能在C++中使用或者耳闻,却在Java中没有见过,因为Java并不支持运算符重载,可能和Java严格的OOP思想有关,有的人可能会说Java中的字符串拼接难道不是运算符重载吗?剥开编译后的.class文件可知,String类型的a+b的实现其实是new StringBuilder(a).append(b).toString()
实现的,并不是真正意义上的运算符重载。
Kotlin中的运算符重载的本质是方法重载;格式很简单,与扩展方法的写法类似:
operator fun String.plus(target: Array<String>): ArrayList<String> { return this.extend(*target)}以上就是对String的加法运算进行重载,其中operator为关键字,表示这是一个重载方法,plus为重载的符号对应的方法名,显然+对应的是plus,其他符号也有对应的方法,下面列举的是一些常用的方法:
| 符号 | 方法 |
|---|---|
| a + b | a.plus(b) |
| a - b | a.minus(b) |
| a * b | a.times(b) |
| a / b | a.div(b) |
| a % b | a.mod(b) |
| a..b | a.rangeTo(b) |
| +a | a.unaryPlus() |
| -a | a.unaryMinus() |
| !a | a.not() |
实战:实现ArrayList的重载
有时候,我们需要在多个字符串中同时追加一段相同的内容,这个当然可以用循环实现并且封装为一个扩展方法,为了使最终代码更直观,我们使用运算符重载来实现:
operator fun ArrayList<String>.plus(target: String): ArrayList<String> { val res = ArrayList<String>() this.forEach { i -> res.add(i + target) } return res}在方法内部,初始化一个新的ArrayList,并将原ArrayList中的字符串遍历拼接然后添加进新的ArrayList中,返回新的ArrayList。
快速初始化一个包含字符串hello0到hello9的ArrayList来测试:
fun main(args: Array<String>) { val a = ArrayList<String>() a.addAll(Array(10) { i -> "hello$i" }) print(a + " world")}同理,可以用运算符重载简化更繁杂的代码,但同时要注意:不要滥用运算符重载,这可能导致很多问题,例如你覆盖了原本某个类的运算符的含义但是却忘记了此事,或者你的小组内其他成员不知道你在某处重载了运算符,这也许也是Java为什么没有提供运算符重载的原因。