作者:Antonio Leiva
时间:Feb 8, 2017
原文链接:https://antonioleiva.com/generic-functions-kotlin/
Kotlin的一些特性组合起来与泛型混合使用创建函数可以极大的简化你的编码,且保证它的可读性。
在Kotlin库中,有几个函数非常实用,一旦你掌握它们的概念使用起来就非常容易。
尽管Kotlin标准库中有几个相似的函数,但是,我计划聚焦在with的各个部分上。
这个函数允许做什么?用了它,我们可以用一变量的代码块作为其上下文,这样就不需要每次使用它重复它的名字。
它们可以替代构建器,不需要为每个类创建特定构建器。
例如,回到前面文章的ViewGroup例子,可以转换这行代码:
1 val childViews = (0..viewGroup.childCount - 1).map { viewGroup.getChildAt(it) }
到:
1 with(viewGroup) { 2 val childViews = (0..childCount - 1).map { getChildAt(it) } 3 }
如你所见,括号内代码的行为就像是其在本类中。
那我们如何得到这样呢?我们在之前的扩展函数中已经见过了。
你能够定义扩展函数为另一个函数的参数。
你怎样实现with函数执行前面的例子?最简单就是这样:
1 inline fun with(view: ViewGroup, f: ViewGroup.() -> Unit) { 2 view.f() 3 }
上面代码以参数形式接收ViewGroup,且一个用于ViewGroup的扩展函数。ViewGroup可无障碍执行那个函数。
但是,这限制较多。对数据的每种类型我们需要一个类似的函数?
当然不是。
我们能够十分容易地用泛型转换上面的函数。只需用T替换ViewGroup:
1 inline fun with(obj: T, f: T.() -> Unit) { 2 obj.f() 3 }
现在它就可以用于任何类型。例如:
1 with(textView) { 2 text = "Hello World" 3 visibility = View.VISIBLE 4 textSize = sp(14).toFloat() 5 }
但是,在我们开始讨论时,我们忽略一项重要的能力:构建器的角色。
如果我们想要一个真正的构建器,我们就需要以某种方式返回构建值:
1 inline fun with(obj: T, f: T.() -> Unit): T { 2 obj.f() 3 return obj 4 }
那样,我们的代码应该是这样:
1 val textView = with(TextView(this)) { 2 text = "Hello World" 3 visibility = View.VISIBLE 4 textSize = sp(14).toFloat() 5 }
注:sp() 是Anko库的函数,在这系列文章前面谈论过它。
如果你看函数的正式定义,它非常类似我们已经做的:
1 public inline fun <T, R> with(receiver: T, block: T.() -> R): R = receiver.block()
这里主要的区别在于扩展函数返回的值可能会不同传递参数。
为了获得与使用常规函数的相同结果,我们需要这样做:
1 val textView = with(TextView(this)) { 2 text = "Hello World" 3 visibility = View.VISIBLE 4 textSize = sp(14).toFloat() 5 this 6 }
最后一行意思是将返回执行扩展函数的对象。
有一个函数功能非常类似我们在前一节得到的,它叫apply。
这个函数作为对象的扩展函数,而不是对象的参数:
1 val textView = TextView(this).apply { 2 text = "Hello World" 3 visibility = View.VISIBLE 4 textSize = sp(14).toFloat() 5 }
如果相应的对象不是null,它将只需内部函数的代码:
1 textView?.text?.let { toast(it) }
仅当TextView和text都不为null,text将被显示在消息框(toast)中。
结合扩展函数,利用泛型类型的能力,我们能做一些非常有趣的事情。
我鼓励你创建自己的函数以简化你的日常工作。