我正在定义一个带有类型参数的抽象类,以及设置此类型的几个具体类。
abstract class GenericFoo[T: ClassTag] {
def defaultValue: T
def useTheValue(v: T): T
}
object DoubleFoo extends GenericFoo[Double] {
def defaultValue = 42.0
def useTheValue(v: Double) = v * 2
}
object IntFoo extends GenericFoo[Int] {
def defaultValue = 1337
def useTheValue(v: Int) = v + 64
}
但是当我将我的Foos存储在一个混合集合中时,我的类型参数T
似乎总是最终被推断为Any
。
val fooCollection:List[GenericFoo[_]] = List(DoubleFoo, IntFoo)
for {
foo <- fooCollection
defaultValue = foo.defaultValue
result = foo.useTheValue(defaultValue)
// Error: `defaultValue` has type `Any`, cannot call `useTheValue`
} yield result
根据类似问题的几个答案,我想使用类型参数通配符(GenericFoo[_]
),也许ClassTag
可以帮助保存类型信息,但我无法让这个例子工作,因为我想要的。
在上面的代码段中,我希望foo.defaultValue
被识别为具有适用于T
的正确类型(foo.useTheValue()
)。我错过了什么吗?
编辑:@ gzm0提示建议我使用抽象类型成员。我原来的用例更多一些,因为我也为一个伴侣对象定义了一个抽象类型。我的实际代码类似于:
trait GenericCompanion[T] {
// Some "static" members
def defaultValue: T
def useTheValue(v: T): T
}
abstract class GenericFoo[T] {
def getCompanion: GenericCompanion[T]
// Because of this operation, it's important that we have the same type `T`
def useCompanion: T = getCompanion.useTheValue(theActualValue)
val theActualValue: T
}
object ConcreteCompanion[Double] extends GenericCompanion[Double] {
// ...
}
object ConcreteFoo[Double] extends GenericFoo[Double] {
def getCompanion = ConcreteCompanion
}
GenericFoo[T]
的每个具体实现都附带CompanionFoo[T]
。应该实例化GenericFoo
的具体子类,而CompanionFoo[T]
对象在这里保存“静态”属性和操作。在此上下文中,GenericFoo
中的类型参数与GenericCompanion
中的类型参数相同非常重要。
(我希望这很清楚,如果我的例子令人费解,那就很抱歉!)
答案 0 :(得分:1)
这是抽象类型成员的一个例子。试试这个:
abstract class GenericFoo {
type V
def defaultValue: V
def useTheValue(v: V): V
}
object DoubleFoo extends GenericFoo {
type V = Double
def defaultValue = 42.0
def useTheValue(v: Double) = v * 2
}
object IntFoo extends GenericFoo {
type V = Int
def defaultValue = 1337
def useTheValue(v: Int) = v + 64
}
val fooCollection:List[GenericFoo] = List(DoubleFoo, IntFoo)
其余代码保持不变。
如果使用抽象类型成员(而不是类型参数),Scala将跟踪它对给定值知道的具体类型。
从你的例子中解释:
val foo: GenericCollection = ???
val defaultValue = foo.defaultValue
// defaultValue: foo.V
foo.useTheValue(defaultValue)
// foo.useTheValue: (foo.V): foo.V
虽然编译器不知道真实类型foo.V
是什么,但它仍然可以确定类型签名匹配,并且使用useTheValue
调用defaultValue
是正常的。
据我所知,这也是一个完全可靠的推理类型参数,Scala只是不这样做。
<强>更新强>
即使使用了更为复杂的示例,您仍然可以这样做:
trait GenericCompanion {
type T
// Some "static" members
def defaultValue: T
def useTheValue(v: T): T
}
abstract class GenericFoo { self =>
type T
def getCompanion: GenericCompanion { type T = self.T }
// Because of this operation, it's important that we have the same type `T`
def useCompanion: T = getCompanion.useTheValue(theActualValue)
val theActualValue: T
}
具体实施:
object ConcreteCompanionDouble extends GenericCompanion {
type T = Double
def defaultValue: Double = ???
def useTheValue(v: Double): Double = ???
// ...
}
object ConcreteFooDouble extends GenericFoo {
type T = Double
def getCompanion = ConcreteCompanionDouble
val theActualValue: Double = ???
}