Scala中的结构类型调度

时间:2009-09-16 20:04:58

标签: scala

我正在努力更好地掌握结构类型调度。例如,假设我有一个带有summary方法的可迭代对象来计算平均值。所以o.summary()给出了列表的平均值。我可能想使用结构类型调度来启用summary(o)

  1. 是否有一组关于o.summary()summary(o)的最佳做法?
  2. 如果我有方法summary(o)summary(o: ObjectType)
  3. ,scala如何解析summary(o: { def summary: Double})
  4. 结构类型调度与多方法或通用函数有何不同?
  5. 迈克尔·加尔平提供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
    

3 个答案:

答案 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的有趣名称的类。)

如果你考虑一下,这并不奇怪。没有办法进行常规方法调用,因为没有通用接口或超类。

所以,其他受访者绝对正确,除非你必须,否则你不想这样做。成本非常高。