我正在尝试实现一个结果缓存(Loader
类),它将类Resolver[T]
的可哈希实例映射到Result
个实例,其中T
的类型为{{ 1}}。当然,用户可以专门提供Result
类,提供特定于应用程序的额外方法。关键是使用Result
方法,用户还必须指定结果类型:
Loader.get
我想要的是避免强制转换并使其隐含,因此代码应该类似于:
val s = Loader.get(new SpecializedResolver()).asInstanceOf[SpecializedResult]
我目前的代码如下:
val implicitS = Loader.get(new SpecializedResolver())
由于我是scala语言的新手,我的问题是:是否有可能通过使用scala提供的更高级的类型功能来实现这种隐式转换?我看了一下演讲'High Wizardry in the Land of Scala',演讲者Daniel Spiewak介绍了一个高阶import scala.language.existentials
abstract class Result {
def computed: Boolean
}
abstract class Resolver[T <: Result] {
def result(): T forSome {type T <: Result}
}
class SpecializedResult extends Result {
def computed = true
def special() = 42
}
class SpecializedResolver extends Resolver[SpecializedResult] {
def result = new SpecializedResult
}
object Loader {
val cache = collection.mutable.Map[K forSome {type K <: Resolver[_]}, V forSome {type V <: Result}]()
def get[K <: Resolver[_]](k: K): V forSome {type V <: Result} = {
cache.getOrElseUpdate(k, { k.result } )
}
}
object Runner {
def main(args: Array[String]) {
val s = Loader.get(new SpecializedResolver()).asInstanceOf[SpecializedResult]
println("Value is: %d".format(s.special))
val implicitS = Loader.get(new SpecializedResolver())
// test.scala:34: error: value special is not a member of Result
println("Value is: %d".format(implicitS.special))
}
}
,它做了类似的事情,但我仍然不太清楚如何使他的解决方案适应我的情况。
答案 0 :(得分:3)
首先,你不需要这里所有的存在。在某些情况下,它们只是过于冗长 - 例如,K forSome { type K <: Resolver[_] }
与K <: Resolver[_]
完全相同。在其他情况下,他们会引入似乎无意的行为。例如:
class FooResult extends Result { def computed = true }
class BarResult extends Result { def computed = true }
object Foo extends Resolver[FooResult] { def result() = new BarResult }
这样编译得很好,因为返回类型T
中的result
会影响类的类型参数T
。
接下来,您是否考虑过懒惰值?他们会为您处理这种缓存 - 您只需在result
实现中将lazy val result = ...
定义为Resolver
,结果将只计算一次(首次需要时)。
如果您决定需要手动处理此缓存,那么您可以通过强制转换来欺骗编译器。例如,给定以下设置:
trait Result { def computed: Boolean }
trait Resolver[T <: Result] { def result(): T }
我们可以写:
object Loader {
private[this] val cache = collection.mutable.Map.empty[Resolver[_], Result]
def get[V <: Result](k: Resolver[V]): V =
cache.getOrElseUpdate(k, k.result()).asInstanceOf[V]
}
我们知道类型会有效,因为对只有一种方法可以进入地图。
更新:我的Loader
解决方案基本上与谢尔盖相同(请注意,锁定地图是个好主意,并且在result
调用时不需要括号,因为getOrElseUpdate
的第二个参数是按名称)。无论如何我都会留下我的答案,因为前半部分是重要的部分。
答案 1 :(得分:2)
在您的情况下,您可以使用简单的解决方案(仅更改代码):
abstract class Resolver[T <: Result] {
def result(): T
}
object Loader {
val cache = collection.mutable.Map[Resolver[_], Any] ()
def get[K <: Result](k: Resolver[K]): K = {
cache.getOrElseUpdate(k, { k.result } ).asInstanceOf[K]
}
}