文章目录
本章总结 : 读懂 apply 标准库函数
public inline fun <T> T.apply(block: T.() -> Unit): T
核心是其 block: T.() -> Unit 参数 , 这是 泛型扩展匿名函数 ;
泛型扩展匿名函数 T.() -> Unit 演变路径 :
在 Kotlin 中 , 定义的 标准库函数 apply 函数 , 函数原型如下 :
public inline fun <T> T.apply(block: T.() -> Unit): T {
block()
return this
}
其中接收参数 类型为 T.() -> Unit 的 泛型扩展匿名函数 , 暴露 接收者 的 函数特性 , 以便于 使用 Lambda 表达式 读取 和 配置 接收者对象 ;
"123".apply {
println(this)
}
上述写法 是 一种 Kotlin 提供的 编程范式 , 该编程范式 暴露 接收者 的 函数特性 , 以便于 使用 Lambda 表达式 读取 和 配置 接收者对象 ; , 借助该编程范式 , 可以写出 DSL 领域特定语言 ;
在 apply 函数 中 支持 接收者对象 的 隐式调用 ;
如下所示 : 调用 “123” 字符串 的 apply 扩展函数 , 在函数的闭包参数中 , this 就是 接收者 “123” 字符串 , 在该 Lambda 表达式中可以 直接调用字符串的方法 ;
因此 , 调用 println(this) 代码 , 打印 this 就是打印 “123” 字符串 ;
调用 length 就是 调用 this.length , 获取 “123” 字符串 的长度 ;
fun main() {
"123".apply {
println(this)
var strLen = length
println(strLen)
}
}
apply 函数原型如下 : 该函数定义在 Standard.kt 脚本中 , 是一个 泛型扩展函数 , 所有的类型都可以使用该扩展函数 ;
/**
* Calls the specified function [block] with `this` value
* as its receiver and returns `this` value.
* 以' this '值作为接收者调用指定函数[block],并返回' this '值。
*
* For detailed usage information see the documentation for [scope functions]
* (https://kotlinlang.org/docs/reference/scope-functions.html#apply).
*/
@kotlin.internal.InlineOnly
public inline fun <T> T.apply(block: T.() -> Unit): T {
contract {
callsInPlace(block, InvocationKind.EXACTLY_ONCE)
}
block()
return this
}
在 apply 函数中 , 接收的 参数类型是 block: T.() -> Unit , 这是一个 Lambda 表达式 / 匿名函数 / 闭包 ,
该 Lambda 表达式 block 类型是 T.() -> Unit , 其 返回值是 Unit 类型 , 表示没有返回值 ;
最终为 泛型 T 定义的泛型扩展函数 为 fun T.apply(block: T.() -> Unit): T , 其 返回的是 T 类型 , 也就是 接收者 本身 ;
继续分析 apply 函数的 参数 T.() -> Unit 类型的 Lambda 表达式 block , 该 Lambda 表达式没有返回值 ,
函数类型是 (参数类型列表) -> 返回值类型 , 如 :
可参考 【Kotlin】Kotlin 函数总结 ( 具名函数 | 匿名函数 | Lambda 表达式 | 闭包 | 内联函数 | 函数引用 ) 博客进行理解 ;
如果泛型扩展函数是 :
fun <T> T.apply(block: () -> Unit): T
就很容易理解 , 去掉参数类型 T.() -> Unit 中的 T. , 上述函数接收的就是一个 参数为空 , 返回值为空 的 Lambda 表达式 ;
在回忆下扩展函数 , 为现有的类定义扩展函数 , 如 : 为 String 定义扩展函数 ;
下面的代码中 , String.addStr 是 为 String 类型添加一个 扩展函数 addStr ;
/**
* 为 String 定义扩展函数, 拼接原字符串和扩展函数参数, 并将结果返回
*/
fun String.addStr(str: String): String {
println("this = $this, string = $str")
return this + str
}
fun main() {
println("123".addStr("abc"))
}
参考 【Kotlin】扩展函数总结 ( 超类扩展函数 | 私有扩展函数 | 泛型扩展函数 | 扩展属性 | 定义扩展文件 | infix 关键字用法 | 重命名扩展函数 | Kotlin 标准库扩展函数 ) 博客就进行理解 ;
为泛型添加扩展函数 称为 泛型扩展函数 ,格式为 :
fun <T> T.函数名(参数列表): T {
函数体
}
如 : 为泛型 T 添加扩展函数 addStr , 没有参数 , 没有返回值 , 即 返回 Unit 类型返回值 , 代码如下 :
fun <T> T.addStr(): Unit {
//函数体
}
该 泛型扩展函数 的 类型 就是 apply 函数的 Lambda 表达式参数类型 T.() -> Unit ;
参考 【Kotlin】扩展函数总结 ( 超类扩展函数 | 私有扩展函数 | 泛型扩展函数 | 扩展属性 | 定义扩展文件 | infix 关键字用法 | 重命名扩展函数 | Kotlin 标准库扩展函数 ) 博客就进行理解 ;
扩展函数 和 匿名函数 是可以组合的 ; 扩展函数也可以是匿名函数 , 匿名函数也可以是扩展函数 ;
T.() -> Unit 的 函数类型是 泛型扩展匿名函数 , 这是 为 泛型 定义的 扩展函数 , 并且该扩展函数 是 匿名函数 ;
与 匿名函数 对应的是 具名函数 , 与 扩展函数 对应的是 原有函数 , 与 泛型 对应的是 具体类型 , 因此 三者是可以任意组合的 ;
这个匿名函数类型 T.() -> Unit 叠了三层 BUFF ;
泛型扩展函数匿名函数 T.() -> Unit 演变路径 :
再次回到 apply 标准库函数 , 分析其函数原型 :
public inline fun <T> T.apply(block: T.() -> Unit): T
该函数的参数是一个 Lambda 表达式 / 匿名函数 / 闭包 , 类型为 T.() -> Unit , 这是一个 泛型扩展匿名函数 类型 , 为 泛型 T 定义的扩展函数 , 同时 T 还是接收者类型 , 返回类型 ;
泛型扩展函数类型的匿名函数 与 普通匿名函数 对比 : apply 函数 传入了 泛型扩展匿名函数 类型 T.() -> Unit 的参数 , 而不是传入一个普通的 匿名函数 () -> Unit ;
如果要 在 不使用 泛型扩展函数 的 前提下 , 达到上述 在 Lambda 表达式中 通过 this 调用 接收者 的效果 , 那么就需要使用 普通类型的 匿名扩展函数 ;
如 : 要想在 String 类型的 apply 扩展函数 的 闭包参数 中 通过 this 来调用 接收者 , 此时就必须使用 如下形式的 标准库 函数 ;
public inline fun String.apply(block: String.() -> Unit): String
一旦写成上述的代码样式 , 只有 String 类型可以调用 apply 函数 , 其它类型就无法调用该函数了 ;
代码示例 : 在下面的代码中 , apply 函数的 参数是 () -> Unit 类型 , 这是 普通的匿名函数 , 在该闭包中无法调用 this ;
public inline fun <T> T.apply(block: () -> Unit): T {
println("调用普通匿名函数")
block()
return this
}
fun main() {
"123".apply {
println(this)
}
}
一旦调用 this , 在编译时就会报错 , 提示如下错误 :
'this' is not defined in this context

这种情况下 , 只能 在匿名函数中通过变量名 , 调用外部的变量 ;
代码示例 :
public inline fun <T> T.apply(block: () -> Unit): T {
println("调用普通匿名函数")
block()
return this
}
fun main() {
var str = "123"
str.apply {
println(str)
}
}
执行结果 : 打印 this , 可以直接将接收者打印出来 ;
调用普通匿名函数
123

代码示例 : 如果要 在匿名函数中使用 this 关键字访问接收者 , 那么必须将其定义为扩展函数 ;
public inline fun String.apply(block: String.() -> Unit): String {
println("调用扩展匿名函数")
block()
return this
}
fun main() {
var str = "123"
str.apply {
println(this)
}
}
执行结果 :
调用扩展匿名函数
123

代码示例 : 在下面的代码中 , 自定义了 apply 函数 , 其接收 泛型扩展函数类型的匿名函数 参数 , 类型为 T.() -> Unit , 在调用时 , 可以在 apply 函数的 Lambda 表达式中使用 this 调用接收者 ;
public inline fun <T> T.apply(block: T.() -> Unit): T {
println("调用自定义泛型扩展函数")
block()
return this
}
fun main() {
"123".apply {
println(this)
}
}
执行结果 : 打印 this , 可以直接将接收者打印出来 ;
调用自定义泛型扩展函数 :
123

假设我做了一个模块如下:m=Module.newdoclassCendend三个问题:除了对m的引用之外,还有什么方法可以访问C和m中的其他内容?我可以在创建匿名模块后为其命名吗(就像我输入“module...”一样)?如何在使用完匿名模块后将其删除,使其定义的常量不再存在? 最佳答案 三个答案:是的,使用ObjectSpace.此代码使c引用你的类(class)C不引用m:c=nilObjectSpace.each_object{|obj|c=objif(Class===objandobj.name=~/::C$/)}当然这取决于
我正在编写一个包含C扩展的gem。通常当我写一个gem时,我会遵循TDD的过程,我会写一个失败的规范,然后处理代码直到它通过,等等......在“ext/mygem/mygem.c”中我的C扩展和在gemspec的“扩展”中配置的有效extconf.rb,如何运行我的规范并仍然加载我的C扩展?当我更改C代码时,我需要采取哪些步骤来重新编译代码?这可能是个愚蠢的问题,但是从我的gem的开发源代码树中输入“bundleinstall”不会构建任何native扩展。当我手动运行rubyext/mygem/extconf.rb时,我确实得到了一个Makefile(在整个项目的根目录中),然后当
我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re
我正在尝试用ruby中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了
我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin
我没有理解以下行为(另请参阅inthisSOthread):defdef_testputs'def_test.in'yieldifblock_given?puts'def_test.out'enddef_testdoputs'def_testok'endblock_test=procdo|&block|puts'block_test.in'block.callifblockputs'block_test.out'endblock_test.calldoputs'block_test'endproc_test=procdoputs'proc_test.in'yieldifblock_gi
我想这样组织C源代码:+/||___+ext||||___+native_extension||||___+lib||||||___(Sourcefilesarekeptinhere-maycontainsub-folders)||||___native_extension.c||___native_extension.h||___extconf.rb||___+lib||||___(Rubysourcecode)||___Rakefile我无法使此设置与mkmf一起正常工作。native_extension/lib中的文件(包含在native_extension.c中)将被完全忽略。
我正在尝试找出如何为我的Ruby项目创建一种“无类DSL”,类似于在Cucumber步骤定义文件中定义步骤定义或在Sinatra应用程序中定义路由。例如,我想要一个文件,其中调用了我的所有DSL函数:#sample.rbwhen_string_matches/hello(.+)/do|name|call_another_method(name)end我认为用我的项目特有的一堆方法污染全局(内核)命名空间是一种不好的做法。因此方法when_string_matches和call_another_method将在我的库中定义,并且sample.rb文件将以某种方式在我的DSL方法的上下文中
如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只
在读取/解析文件(使用Ruby)时忽略某些行的最佳方法是什么?我正在尝试仅解析Cucumber.feature文件中的场景,并希望跳过不以Scenario/Given/When/Then/And/But开头的行。下面的代码有效,但它很荒谬,所以我正在寻找一个聪明的解决方案:)File.open(file).each_linedo|line|line.chomp!nextifline.empty?nextifline.include?"#"nextifline.include?"Feature"nextifline.include?"Inorder"nextifline.include?