我正在努力更好地掌握结构类型调度。例如,假设我有一个带有summary
方法的可迭代对象来计算平均值。所以o.summary()
给出了列表的平均值。我可能想使用结构类型调度来启用summary(o)
。
o.summary()
与summary(o)
的最佳做法?summary(o)
和summary(o: ObjectType)
summary(o: { def summary: Double})
迈克尔·加尔平提供following discription关于structural type发送的信息:
结构类型是Scala的“响应式”样式编程版本,如许多动态语言所示。所以喜欢
def sayName ( x : { def name:String }){
println(x.name)
}
然后任何带有名为name的方法的对象都没有参数并返回一个字符串,可以传递给sayName:
case class Person(name:String)
val dean = Person("Dean")
sayName(dean) // Dean
答案 0 :(得分:3)
--
1.在您的示例中,我不会使用summary(o)
版本,因为这不是一种非常面向对象的编程风格。在调用o.summary
时(您可以删除括号,因为它没有副作用),您要求summary
属性为o
。在调用summary(o)
时,您将o
传递给计算o
摘要的方法。我相信第一种方法更好:)。
我没有太多地使用结构类型调度,但我认为它最适合(在大型系统中),因为一个方法需要一个定义了某个方法的类型,你必须编写一个接口。有时创建该接口并强制客户端实现它可能很尴尬。有时您希望使用在另一个API中定义的客户端,该客户端符合您的接口但未明确实现它。因此,在我看来,结构类型调度是一种很好的方式来隐式地生成适配器模式(保存在样板上,是的!)。
--
2.显然,如果您致电summary(o)
且o
属于ObjectType
,则summary(o: ObjectType)
会被调用(这有意义)。如果您致电summary(bar)
,其中bar
不属于ObjectType
,则可能会发生两件事。如果bar
具有正确签名和名称的方法summary()
,则调用将编译,否则调用将无法编译。
示例:
scala> case class ObjectType(summary: Double)
defined class ObjectType
scala> val o = ObjectType(1.2)
o: ObjectType = ObjectType(1.2)
scala> object Test {
| def summary(o: ObjectType) { println("1") }
| def summary(o: { def summary: Double}) { println("2")}
| }
defined module Test
scala> Test.summary(o)
1
不幸的是,由于类型擦除,以下内容无法编译:
scala> object Test{
| def foo(a: {def a: Int}) { println("A") }
| def foo(b: {def b: Int}) { println("B") }
| }
:6: error: double definition:
method foo:(AnyRef{def b(): Int})Unit and
method foo:(AnyRef{def a(): Int})Unit at line 5
have same type after erasure: (java.lang.Object)Unit
def foo(b: {def b: Int}) { println("B") }
--
3.从某种意义上说,结构类型调度比泛型方法更具动态性,并且还有不同的用途。在通用方法中,您可以说:a。我想要任何类型的东西;湾我想要一种类型为A的子类型; C。我会拿一些超B型的东西; d。我将采用隐式转换为C类的东西。所有这些都比“我想要一个具有正确签名的方法foo
的类型更严格”。此外,结构类型调度确实使用反射,因为它们是通过类型擦除实现的。
我对多方法知之甚少,但是看wikipedia article,似乎可以使用模式匹配来实现Scala中的多方法。例如:
def collide(a: Collider, b: Collider) = (a, b) match {
case (asteroid: Asteroid, spaceship: Spaceship) => // ...
case (asteroid1: Asteroid, asteroid2: Asteroid) => // ...
...
同样,您可以使用结构类型调度 - def collide(a: {def processCollision()})
,但这取决于设计决策(我会在此示例中创建一个接口)。
-- Flaviu Cipcigan
答案 1 :(得分:3)
结构数据类型实际上并不是那么有用。这并不是说它们没用,但它们绝对是一个利基市场。
例如,您可能希望为某些内容的“size
”编写一个通用测试用例。你可以这样做:
def hasSize(o: { def size: Int }, s: Int): Boolean = {
o.size == s
}
这可以与任何实现“size”方法的对象一起使用,无论其类层次结构如何。
现在,它们不是结构类型调度。它们与调度无关,而与类型定义无关。
Scala始终是面向对象的语言。您必须调用对象上的方法。函数调用实际上是“apply
”方法调用。像“println
”这样的东西只是导入范围的对象的成员。
答案 2 :(得分:0)
我想你问过Scala对结构类型的调用是做什么的。它使用反射。例如,考虑
def repeat(x: { def quack(): Unit }, n: Int) {
for (i <- 1 to n) x.quack()
}
调用x.quack()
被编译成quack方法的查找,然后使用Java反射进行调用。 (您可以通过使用javap查看字节代码来验证这一点。查找一个名为Example$$anonfun$repeat$1
的有趣名称的类。)
如果你考虑一下,这并不奇怪。没有办法进行常规方法调用,因为没有通用接口或超类。
所以,其他受访者绝对正确,除非你必须,否则你不想这样做。成本非常高。