【问题标题】:Get actually type from map with generic keys and values使用通用键和值从映射中获取实际类型
【发布时间】:2017-12-18 21:54:15
【问题描述】:

首先是一些 Scala 代码示例:

trait Key[+T]
trait Value[+T]

case class StringKey(key: String) extends Key[String]
case class IntKey(key: Int) extends Key[Int]

case class StringValue(value: String) extends Value[String]
case class IntValue(value: Int) extends Value[Int]

case class ValueHolder(sources: Map[Key[_], Value[_]]) {
  def processSources(): Unit = {
    sources.map{ case (key, value) =>
      // get actual key and value, and process
    }
  }
}

基本上我有一个从通用键到通用值的映射,我需要遍历键值对并对实际对象进行操作。例如,如果当前值实际上是StringValue,我想对StringValue 对象进行操作。有没有办法做到这一点?

【问题讨论】:

  • 鉴于泛型的 JVM 类型擦除,我认为不可能这样。但是,如果您无论如何都将您的键和值包装在其他类中,为什么不将类型信息也存储在那里呢?
  • @SergGr 我认为他已经在做包装的事情了。我发布了工作解决方案。如果我缺少其他任何东西,我感谢您的建议

标签: scala generics scala-collections


【解决方案1】:

是的。当StringValue 发生时使用模式匹配或使用Partial 函数收集结果。

首先,我想你错过了StringKey extends Key[String]T 的协方差等等......

下面的代码告诉我们需要什么。

trait Key[+T]

trait Value[+T]

case class StringKey(key: String) extends Key[String]
case class IntKey(key: Int) extends Key[Int]

case class StringValue(value: String) extends Value[String]
case class IntValue(value: Int) extends Value[Int]

假设上述情况

case class ValueHolder(sources: Map[Key[_], Value[_]]) {

  def processSources(): Unit = {
    sources
     .collect { case (_, StringValue(str)) => str }
     .foreach(println)
  }

}

Scala REPL

scala> :paste
// Entering paste mode (ctrl-D to finish)


    trait Key[+T]

    trait Value[+T]

    case class StringKey(key: String) extends Key[String]
    case class IntKey(key: Int) extends Key[Int]

    case class StringValue(value: String) extends Value[String]
    case class IntValue(value: Int) extends Value[Int]

// Exiting paste mode, now interpreting.

defined trait Key
defined trait Value
defined class StringKey
defined class IntKey
defined class StringValue
defined class IntValue

scala> :paste
// Entering paste mode (ctrl-D to finish)

case class ValueHolder(sources: Map[Key[_], Value[_]]) {

      def processSources(): Unit = {
        sources
         .collect { case (_, StringValue(str)) => str } // extracted string value ready to be operated.
         .foreach(println)
      }

    }

// Exiting paste mode, now interpreting.

defined class ValueHolder

输出

scala> ValueHolder(Map(StringKey("fruit") -> StringValue("apple")))
res2: ValueHolder = ValueHolder(Map(StringKey(fruit) -> StringValue(apple)))

scala> ValueHolder(Map(StringKey("fruit") -> StringValue("apple"))).processSources()
apple

一般来说,您可以迭代键值对

给定sources: Map[Key[_], Value[_]]

如果值为字符串值则处理

sources.map { case (key, value) =>
  value match {
    case stringValue: StringValue => processValue(stringValue)
    case _ => fallbackValue
  }
}

更好的方法是使用collect

sources.collect { case (_, value: StringValue) => processValue(value) }

【讨论】:

  • 谢谢。那行得通,但这不是我想要的(对不起,我没有在问题中说清楚)。我想知道是否有可能在没有模式匹配的情况下得到它。原因是在流程循环中,我需要调用def processValue[T](key: Key[T]): SomeOtherType[T] 之类的方法来获取 T 的值并执行该流程以生成一些与 T 相关的对象。由于对 processValue 的调用除了我想要的类型参数之外是相同的知道是否有办法在没有模式匹配的情况下直接做到这一点。
  • @JackLei 你可以从这里做任何事情。我不明白你可以处理什么。你能说清楚你想要什么作为最终结果。例如“我想处理/获取字符串值为‘apple’的‘key’”
  • @JackLei 在答案末尾添加了更多信息。希望这能解决你的问题;
  • @JackLei 这是你的问题的答案sources.collect { case (_, value: StringValue) => processValue(value) }
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-16
  • 1970-01-01
  • 1970-01-01
  • 2020-10-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多