Ali*_*ina 13 scala anonymous-inner-class
我正在阅读"Scala for the Impatient",他们在8.8中说:
[..]你可以使用abstract关键字来表示一个无法实例化的类[..]
abstract class Person { val id: Int ; var name: String }
Run Code Online (Sandbox Code Playgroud)
几行之后:
您始终可以使用匿名类型自定义抽象字段:
val fred = new Person {
val id = 1729
var name = "Fred"
}
Run Code Online (Sandbox Code Playgroud)
因此,他们用匿名类型人工实例化Person类.在现实世界的哪种情况下,人们会想要这样做?
在稍微思考一下我自己的答案之后,我得出的结论是,它所说的基本上只是:
“匿名本地类实例是穷人的函数文字”
+150为有助于扩大这一狭隘视野的答案提供悬赏。
长话短说
每当您想要将方法的实现视为对象时,您可以实例化一个扩展抽象基类的匿名本地类,实现方法,然后像基类的任何其他实例一样传递创建的实例。
概述
这篇文章讨论了您可能想要实例化匿名本地类的五种情况。这些示例从非常基础到相当高级。
RunnableFunction<X, Y>免责声明:有些代码不惯用,因为它“重新发明轮子”并且不会隐藏 lambda 或 SingleAbstractMethod 语法中抽象本地类的实例化。
简单的介绍性示例:Runnable
假设您要编写一个方法,该方法采用某些代码块并多次执行它:
def repeat(numTimes: Int, whatToDo: <someCleverType>): Unit = ???
Run Code Online (Sandbox Code Playgroud)
假设您想从头开始重新发明一切,并且不想使用标准库中的任何名称参数或接口,您会用什么来代替<someCleverType>?您必须提供看起来有点像这样的基类:
abstract class MyRunnable {
def run(): Unit // abstract method
}
Run Code Online (Sandbox Code Playgroud)
repeat现在您可以按如下方式实现您的方法:
def repeat(numTimes: Int, r: MyRunnable): Unit = {
for (i <- 1 to numTimes) {
r.run()
}
}
Run Code Online (Sandbox Code Playgroud)
现在假设您想使用此方法来打印“Hello, world!” 十次。如何创造权利MyRunnable?您可以定义一个
HelloWorld扩展MyRunnable并实现该run方法的类,但它只会污染命名空间,因为您只想使用它一次。相反,您可以直接实例化匿名类:
val helloWorld = new MyRunnable {
def run(): Unit = println("Hello, world!")
}
Run Code Online (Sandbox Code Playgroud)
然后将其传递给repeat:
repeat(10, helloWorld)
Run Code Online (Sandbox Code Playgroud)
您甚至可以省略该helloWorld变量:
repeat(10, new MyRunnable {
def run(): Unit = println("Hello, world!")
})
Run Code Online (Sandbox Code Playgroud)
这是一个典型的示例,说明了为什么您想要实例化匿名本地类。
稍微有趣的例子:RealFunction
在前面的示例中,run不带任何参数,它每次都执行相同的代码。
现在我想稍微修改一下示例,以便实现的方法带有一些参数。
我现在不会提供完整的实现,但假设你有一个函数
plot(f: RealFunction): Unit = ???
Run Code Online (Sandbox Code Playgroud)
绘制实数函数的图形R -> R,其中RealFunction是一个抽象类,定义为
abstract class RealFunction {
def apply(x: Double): Double
}
Run Code Online (Sandbox Code Playgroud)
要绘制抛物线,您现在可以执行以下操作:
val xSquare = new RealFunction {
def apply(x: Double): Double = x * x
}
plot(xSquare)
Run Code Online (Sandbox Code Playgroud)
您甚至可以单独测试它,而无需plot:例如,p(42)计算1764.0,它是 的平方42。
一般功能Function[X, Y]
前面的示例概括为任意函数,这些函数可以具有类型X和Y作为域和余域。从历史的角度来看,这可以说是最重要的例子。考虑以下抽象类:
abstract class Function[X, Y] {
def apply(x: X): Y // abstract method
}
Run Code Online (Sandbox Code Playgroud)
它与 the 类似,但现在有and ,RealFunction而不是固定的。DoubleXY
xSquare给定此接口,您可以按如下方式重新创建该函数:
val xSquare = new Function[Double, Double] {
def apply(x: Double) = x * x
}
Run Code Online (Sandbox Code Playgroud)
事实上,这个例子非常重要,以至于 Scala 的标准库中充满了这样的FunctionN[X1,...,XN, Y]用于不同数量参数的接口N。
这些接口有自己的简洁语法,并且在编译器中享有很高的特权。从您的问题的角度来看,这会产生一个“问题”,因为匿名类的实例化通常隐藏在特殊的内置语法糖下。在惯用的 Scala 中,你通常会简单地写
val xSquare = (x: Double) => x * x
Run Code Online (Sandbox Code Playgroud)
代替
val xSquare = new Function[Double, Double] {
def apply(x: Double) = x * x
}
Run Code Online (Sandbox Code Playgroud)
其他 JVM 语言的情况也类似。例如,甚至 Java 版本 8 也在java.util.function. 几年前,你会写类似的东西
Function<Integer, Integer> f = new Function<Integer, Integer>() {
public Integer apply(Integer x) {
return x * x;
}
};
Run Code Online (Sandbox Code Playgroud)
在 Java 中,因为还没有 lambda,每次你想传递某种回调 或Runnable或时Function,你都必须实现一个扩展抽象类的匿名类。如今,在较新的 Java 版本中,它被 lambda 和 SingleAbstractMethod 语法隐藏,但原理仍然相同:构造实现接口或扩展抽象类的匿名类实例。
一个高级的“几乎真实世界”示例
在今天编写的代码中您不会遇到任何前面的示例,因为匿名本地类的实例化被 lambda 的语法糖隐藏了。我想提供一个现实的例子,其中匿名本地类的实例化实际上是不可避免的。
new AbstractClassName(){ }在没有可用语法糖的地方,-语法仍然会出现。例如,由于 Scala 没有多态 lambda 语法,因此要在 Scalaz 或 Cats 等库中构造自然转换,您通常会编写如下内容:
val nat = new (Foo ~> Bar) {
def apply[X](x: Foo[X]): Bar[X] = ???
}
Run Code Online (Sandbox Code Playgroud)
在这里,Foo和Bar类似于嵌入式领域特定语言,它们在不同的抽象级别上运行,并且Foo是更高级别的,而Bar是更低级别的。又是同样的原理,这样的例子比比皆是。这是现实世界中几乎“逼真”的用法示例:定义(KVStoreA ~> Id)-interpreter。我希望你能认出new (KVStoreA ~> Id) { def apply(...) ... }里面的部分。不幸的是,这个示例相当高级,但正如我在评论中提到的,在过去的十年中,所有简单且常用的示例大部分都被 lambda 和单一抽象方法语法隐藏了。
回到你的例子
您引用的代码
abstract class Person(val name: String) {
def id: Int
}
val fred = new Person {
val id = 1729
var name = "Fred"
}
Run Code Online (Sandbox Code Playgroud)
似乎无法编译,因为缺少构造函数参数。
我的猜测是作者想证明你可以def通过s 覆盖vals:
trait P {
def name: String
}
val inst = new P {
val name = "Fred"
}
Run Code Online (Sandbox Code Playgroud)
虽然很高兴知道这是可能的,但我不认为这是匿名本地类实例化最重要的用例(因为您可以使用普通成员变量并在构造函数中传递值)。考虑到篇幅限制,本书的作者可能只是想快速演示语法,而不对其实际用法进行深入讨论。
| 归档时间: |
|
| 查看次数: |
743 次 |
| 最近记录: |