基于方法定义泛型类型(结构类型)
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
A
和 B
有 getC
而没有 ()
所以你需要从类型约束中删除 ()
:
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
" 的含义,然后您将为 A
和 B
提供实现。因为 HasC
是你的特质,即使你无法控制 A
和 B
的实现,你也可以这样做。然后,您将定义 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)
请注意,这更通用,因为我从不需要在 A
和 B
上定义 getC
,这就是为什么我将这些方法重命名为 getCFromA
和 getCFromB
.
Typeclasses 是 Scala 中许多函数式编程的基础,类似的概念在其他现代语言中可用,例如 Haskell、Rust(特征)或 Swift (协议)。
我正在尝试声明一个 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
A
和 B
有 getC
而没有 ()
所以你需要从类型约束中删除 ()
:
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
" 的含义,然后您将为 A
和 B
提供实现。因为 HasC
是你的特质,即使你无法控制 A
和 B
的实现,你也可以这样做。然后,您将定义 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)
请注意,这更通用,因为我从不需要在 A
和 B
上定义 getC
,这就是为什么我将这些方法重命名为 getCFromA
和 getCFromB
.
Typeclasses 是 Scala 中许多函数式编程的基础,类似的概念在其他现代语言中可用,例如 Haskell、Rust(特征)或 Swift (协议)。