基于方法定义泛型类型(结构类型)

Defining generic type based on method (structural typing)

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

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 进行更改 class 所以基于特征的解决方案在这里不起作用。

另外,请问有更好的方法吗?

问题是

def getC = 100

的函数签名不同
def getC() = 100

ABgetC 而没有 () 所以你需要从类型约束中删除 ():

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

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

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

在我看来,有一个更优雅的解决方案,它依赖于通常称为 classes 的类型。您将定义一个 HasC 特征来定义它对 "have a C" 的含义,然后您将为 AB 提供实现。因为 HasC 是你的特质,即使你无法控制 AB 的实现,你也可以这样做。然后,您将定义 readC 接受任何类型 T,其中 HasC[T] 隐式可用。 Scala 通过上下文边界支持这一点: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)

请注意,这更通用,因为我从不需要在 AB 上定义 getC,这就是为什么我将这些方法重命名为 getCFromAgetCFromB.

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