【问题标题】:Generic lazy-initialization with scala使用 scala 进行通用延迟初始化
【发布时间】:2013-07-22 22:22:33
【问题描述】:

我不想写很多样板代码,所以我决定为lazy-init编写泛型方法。

import java.util._
import concurrent.ConcurrentHashMap

object GenericsTest {
  val cache: ConcurrentHashMap[Long, 
    ConcurrentHashMap[Long, 
      ConcurrentHashMap[Long, 
        ConcurrentHashMap[Long, Long]]]] = new ConcurrentHashMap()

  def main(args: Array[String]) {
    val x = get(cache, 1)(() => new ConcurrentHashMap())
    val y = get(x, 1)(() => new ConcurrentHashMap())
    val z = get(y, 1)(() => new ConcurrentHashMap())
  }

  def get[B, A](map: ConcurrentHashMap[A, B], a: A)(factory: () => B): B = {
    if (map.containsKey(a)) {
      map.get(a)
    } else {
      val b = factory()
      map.put(a, factory())
      b
    }
  }
}

此示例仅使用硬编码的 Long 运行,而没有使用通用 A,这可能是什么问题?也许还有另一种方法可以做这些事情?

【问题讨论】:

  • 你能准确地说明什么是行不通的,并解释它是如何失败的吗?

标签: scala lazy-initialization type-parameter


【解决方案1】:

错误在这一行:

val x = get(cache, 1)(() => new ConcurrentHashMap())

问题是1的类型是Int

我们有这个方法签名:

get[A, B](map: ConcurrentHashMap[A, B], a: A)(factory: () => B): B

在那个有问题的调用中传递的参数类型是(B 是那个长嵌套类型,现在无关紧要):

ConcurrentHashMap[Long, B] and Int

所以编译器计算出A一定是LongInt最接近的共同祖先,即AnyVal,所以最后它会使用传入的参数类型为:

ConcurrentHashMap[AnyVal, B] and AnyVal

但是 ConcurrentHashMap 的第一个类型参数是不变的,所以 cache val 不能用作 ConcurrentHashMap[AnyVal, B],所以编译器给出了这个错误message(去掉了长嵌套类型参数部分,现在没关系了):

found   : java.util.concurrent.ConcurrentHashMap[Long, ...]
required: java.util.concurrent.ConcurrentHashMap[AnyVal, ...]
Note: Long <: AnyVal, but Java-defined class ConcurrentHashMap is invariant in type K.

要解决此问题,您需要将第二个参数作为 Long 传递:

val x = get(cache, 1L)(() => new ConcurrentHashMap())
val y = get(x, 1L)(() => new ConcurrentHashMap())
val z = get(y, 1L)(() => new ConcurrentHashMap())

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-05-21
    • 2011-06-04
    • 2020-03-19
    • 1970-01-01
    • 2020-05-10
    • 2014-08-23
    • 2013-11-14
    • 1970-01-01
    相关资源
    最近更新 更多