Scala:访问 "static" 有界泛型类型的成员

Scala: access "static" member of bounded generic type

我想实现以下目标:

abstract class Super {
  def typeSpecific: Int
}

class SubA extends Super {
  def typeSpecific = 1
}
class SubB extends Super {
  def typeSpecific = 2
}

class Tester[T <: Super] {
  def test = T.typeSpecific
}

val testerA = new Tester[SubA]
val testerB = new Tester[SubB]

testerA.test // should return 1
testerB.test // should return 2

在 Scala 中可以实现这样的功能吗?这失败了,因为在 Tester.test.

中找不到 T 的值

typeSpecific 不是静态成员,它属于 SubASubB 的实例,您没有。您也不能从类型参数静态访问任何内容(它是一种类型,而不是对象)。

这不会按原样工作,因为您没有 SubASubB 的实例,也无法通过 new Tester[SubA] 获得它们。但是您可以要求 Tester 混合一种 Super 以使其成为一个(因此具有 typeSpecific)。这将需要您将 SuperSubASubB 更改为特征,并且还会使您的实例匿名 类.

trait Super {
  def typeSpecific: Int
}

trait SubA extends Super {
  def typeSpecific = 1
}
trait SubB extends Super {
  def typeSpecific = 2
}

// The self-type `this: A =>` requires the mix-in.
class Tester[A <: Super] { this: A =>
  def test = typeSpecific
}

val testerA = new Tester[SubA] with SubA
val testerB = new Tester[SubB] with SubB

scala> testerA.test
res2: Int = 1

scala> testerB.test
res3: Int = 2

您还可以要求 A <: Super 作为 Tester 的构造函数参数,这可能是更简洁的选项。

abstract class Super {
  def typeSpecific: Int
}

class SubA extends Super {
  def typeSpecific = 1
}
class SubB extends Super {
  def typeSpecific = 2
}

class Tester[A <: Super](s: A) {
  def test = s.typeSpecific
}

val testerA = new Tester(new SubA)
val testerB = new Tester(new SubB)

scala> testerA.test
res5: Int = 1

scala> testerB.test
res6: Int = 2

无论你怎么剪,你都需要一个 SubASubB.

的实例

您将不得不结合使用反射和类型标签来获得您想要的结果。我警告你,它有点难看:

import scala.reflect.runtime.universe._

abstract class SuperClass {
  def typeSpecific: Int
}

class SubA extends SuperClass {
  def typeSpecific = 1
}
class SubB extends SuperClass {
  def typeSpecific = 2
}

class Tester[T <: SuperClass: TypeTag] {
  def test = typeTag[T].mirror.runtimeClass(typeOf[T]).newInstance.asInstanceOf[T].typeSpecific
}

我还觉得我应该提到 typeSpecific 不是静态的,因为它是 class 的一部分,在 scala 中,静态成员仅在 objects/companion 对象中定义。使用对象做这样的事情会更干净:

trait SuperTrait {
  def typeSpecific: Int
}

object SubA extends SuperTrait {
  def typeSpecific = 1
}
object SubB extends SuperTrait {
  def typeSpecific = 2
}

class Tester(testObject : SuperTrait) {
  def test = testObject.typeSpecific
}

new Tester(SubA).test
new Tester(SubB).test