Kotlin创建DSL

<Kotlin从入门进阶到实战>

什么是DSL

DSL(Domain-Specific Language,领域特定语言)指的是专注于特定问题领域的计算机语言。不同于通用的计算机语言,领域特定语言之用在某些特定的领域。DSL语言让我们更简洁的方式来表达和解决领域问题。简单的讲就是对一个特定的问题的方案模型更高层次的抽象表达。使其更加简单易懂。
DSL只是问题解决方案的外部封装。这个模型可能是一个API库,也可能是一个完整的框架。典型的例子是用于替代Android开发中布局XML文件的DSL框架Anko,它使用与基于Kotlin的DSL来盛名Android UI组件,为不是传统的XML。
XML本质上来讲也是一种DSL。DSL有内部DSL和外部DSL之分。

  • 内部DSL
    内部DSL指的是与项目中使用的通用目的语言(java,C#等)紧密相关的一类DSL。它基于通用编程语言来实现。
  • 外部DSL
    内部DSL是利用编程语言的语法结构定义出来的DSL。也叫做内嵌DSL。外部DSL是从零开始构建的语言。需要实现语法分析器等,通常情况下,我们只需要实现内嵌式DSL。因为它更容易构建。并具有很多与外部DSL相同的优势。外部DSL与通用编程语言GPL类似,但是外部DSL更加专注于特定领域。它可以是编译型或者解释型。
Kotlin的DSL特性支持
  • 许多现代语言为创建内部DSL提供了一些先进的方法,Kotlin也不例外。在Kotlin中创建DSL,一般需要下面三个特性:
  1. 扩展函数、扩展属性;
  2. 带接受者的Lambda表达式(高阶函数);
  3. invoke函数调用约定

关于扩展函数和高阶函数之前的博客已经说过。下面讲一下Kotlin中的invoke操作符函数。
在集合类中,我么知道Kotlin使用下标运算符foo[x]来等价调用foo.get(x)操作符函数,同样的关于invoke也有一个类似的预订。对一个predicate:(T)->Boolean,可以直接调用predicate(element),如List的扩展函数filterTo中

public inline fun <T, C : MutableCollection<in T>> Iterable<T>.filterTo(destination: C, predicate: (T) -> Boolean): C {
    for (element in this) if (predicate(element)) destination.add(element)
    return destination
}

Kotlin中foo.invoke()可以写成foo(),在Kotlin中操作符是可以重载的,“()”操作符对应的就是类的重载操作函数invoke,即predicate:(T)->Boolean函数调用

prediacate(element) 等价于  predicate.invoke(element)

上面是函数类型对象invoke函数的例子,而实际上在Kotlin中类的对象中也可以直接使用()操作符来调用这个类的invoke操作符函数。下面是一个例子

class Hello {
        operator fun invoke(name: String) {
            println("Hello,$name")
        }
    }
@JvmStatic
fun main(args: Array<String>) {
     val hello = Hello()
      hello.invoke("aaaaa")
      hello("AAAAA")
}

hello.invoke(“aaaaa”) 、 hello(“AAAAA”) 这两句代都可以正确打印。

实现集合类的流式Kotlin DSL

java的工具类如java.util.Collections,这样的类里面提供了很多静态方法。如:

public static <T> boolean addAll(Collection<? super T> c, T... elements) {
        boolean result = false;
        for (T element : elements)
            result |= c.add(element);
        return result;
}

public static void reverse(List<?> list) {
      //....
}

实际编码中,通常这样调用这些静态方法。

Collections.reverse(list);
Collections.sort(list);
int index = Collections.binarySearch(list,x);

这样调用看起来不算太漂亮,如果我们希望这样调用代码:

list.sort();
list.reverse();

这就是Kotlin中的扩展函数。

fun <T : Comparable<T>> List<T>.sort() {
    Collections.sort(this)
}

有了扩展函数,我们就可以开始流式API 的DSL了。代码风格是下面这样的

fun main(args: Array<String>) {
        val lines =
                "src/main/resource/data.txt"
                        .stream()
                        .buffered()
                        .reader("utf-8")
                        .readLines()
        lines.forEach(::println)
    }
  1. 首先给String类型扩展一个stream()函数:
fun String.stream() = FileInputStream(this)
  1. 然后给FileInputStream扩展一个bufferrd()函数
fun InputStream.buffered() = BufferedInputStream(this)
  1. 再给InputStream扩展一个reader(charset:String)函数
fun InputStream.reader(charset :String) = InputStreamReader(this,charset)
  1. 再给Reader扩展一个readLines()函数
fun Reader.readLines(): List<String> {
    val result = arrayListOf<String>()
    forEachLine { result.add(it) }
    return result
}

有了这些扩展函数,就可以使用开始的流式API。实际上Kotlin中的I/O文件读写及集合类中的流式API就是这么扩展Java。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值