1

我正在尝试声明一个基于某些函数的类型的类。

class A{
  def getC = 10
}

class B {
  def getC = 100
  def getB = 9
}


def readC[T <: {def getC():Int}](obj:T) = {
  obj.getC()
}

val a = new A()
val b  = new B()

readC(a)
readC(b) 

我希望 readC 应该与 A 和 B 一起使用。我也不能对 A 和 B 类进行更改,因此没有基于特征的解决方案可以在这里工作。

另外,有没有更好的方法呢?

4

2 回答 2

4

您正在尝试做的事情称为结构类型,它受 Scala 支持。因为没有实际的类,JVM 上的实现必须依赖反射,所以getC与基于 trait 的解决方案相比,调用会非常慢。在这些情况下调用getC称为反射调用,编译器实际上会发出警告,除非您import scala.language.reflectiveCalls承认自己知道自己在做什么。

在我看来,有一个更优雅的解决方案,它依赖于通常称为类型类的东西。您将定义一个HasCtrait 来定义“拥有一个C”的含义,然后您将为 和 提供A 实现B。因为HasC是你的特质,即使你无法控制 and 的实现,你也可以A做到B。然后,您将定义具有隐式可用的readC任何类型。Scala 通过上下文边界支持这一点:. THasC[T]def readC[T: HasC]

这是一个工作示例:

class A {
  def getCFromA: Int = 10
}

class B {
  def getCFromB: Int = 100
}

trait HasC[T] {
  def c(t: T): Int
}

object HasC {
  implicit object AHasC extends HasC[A] {
    def c(a: A): Int = a.getCFromA
  }
  implicit object BHasC extends HasC[B] {
    def c(b: B): Int = b.getCFromB
  }
} 

def readC[T : HasC](t: T): Int = implicitly[HasC[T]].c(t)

val a = new A()
val b  = new B()

readC(a)
readC(b)

这条线

def readC[T : HasC](t: T): Int = implicitly[HasC[T]].c(t)

只是一种(可以说)更好的写作方式

def readC[T](t: T)(implicit hasC: HasC[T]): Int = hasC.c(t)

请注意,这是更通用的,因为我从不需要getC同时定义Aand B,这就是我将方法重命名为getCFromAand的原因getCFromB

类型类是 Scala 中许多函数式编程的基础,类似的概念在其他现代语言中可用,例如 Haskell、Rust(特征)或 Swift(协议)。

于 2019-08-23T11:53:01.403 回答
3

问题是

def getC = 100

与函数签名不同

def getC() = 100

A并且B没有getC()因此您需要()从类型约束中删除:

def readC[T <: {def getC: Int}](obj: T) = {
  obj.getC
}

更好的方法是使用typeclass。在线查看有关如何执行此操作的许多好的教程。

于 2019-08-23T11:44:22.973 回答