【问题标题】:Scala dynamic return type based on input type基于输入类型的Scala动态返回类型
【发布时间】:2020-05-02 17:56:07
【问题描述】:

好吧,我不知道这段代码有什么问题:

import scala.reflect.runtime.universe._
trait Key extends Product
case class SomeKey(a: Int, b: String) extends Key
case class SomeOtherKey(a: Int, b: String, c:Boolean) extends Key

trait MyTrait[T <: Key] {
  def someField: Int
  def someFunc(implicit tTypeTag: TypeTag[T]): Map[T, Int] = {
    typeOf(tTypeTag) match {
      case t if t =:= typeOf[SomeKey] => Map(SomeKey(1,"2") -> 1)
      case t if t =:= typeOf[SomeOtherKey] => Map(SomeOtherKey(1,"2",true) -> 2)
    }
  }
}

如果从扩展MyTrait[SomeKey] 的案例类中调用someFunc,我希望(示例过于简单)能够返回Map[SomeKey, Int]。并从MyTrait[SomeOtherKey]返回Map[SomeOtherKey, Int]

case class MyClass(val s: Int) extends MyTrait[SomeKey] {
  override def someField = s
}

这里MyClass 的新实例在调用someFunc 时应该返回Map[SomeKey, Int]

但它甚至没有编译,编译器抱怨模式匹配的每一行:

type mismatch;
 found   : (Playground.this.SomeKey, Int)
 required: (T, Int)

type mismatch;
 found   : (Playground.this.SomeOtherKey, Int)
 required: (T, Int)

【问题讨论】:

  • 您能否详细说明您需要了解的有关特定类型的密钥的哪些信息?此外,该方法是否必须在相同的特征中定义?还是可以作为扩展方法添加?

标签: scala types pattern-matching


【解决方案1】:

这是一个使用类型类和隐式的解决方案。

trait Key extends Product
case class SomeKey(a: Int, b: String) extends Key
case class SomeOtherKey(a: Int, b: String, c:Boolean) extends Key

trait TypeClass[T] {
  def someFunc: Map[T, Int]
}
object TypeClass {
  implicit def forSomeKey: TypeClass[SomeKey] = new TypeClass[SomeKey] {
    override def someFunc: Map[SomeKey, Int] = Map(SomeKey(1, "2") -> 1)
  }
  implicit def forSomeOtherKey: TypeClass[SomeOtherKey] = new TypeClass[SomeOtherKey] {
    override def someFunc: Map[SomeOtherKey, Int] = Map(SomeOtherKey(1, "2", true) -> 1)
  }
}

trait MyTrait[T <: Key] {
  def someField: Int
  def someFunc(implicit tc: TypeClass[T]): Map[T, Int] = tc.someFunc
}

【讨论】:

  • 不管它值多少钱,我独立想出了一个相同的答案,并被秒杀。虽然我会打电话给TypeClass 一些更具描述性的东西,例如IntMapProvider 什么的。
  • @JoeK 那将是第三个相同的答案! Mario Galic 的回答更加充实——现在没有必要再完善了。
【解决方案2】:

TypeTag 会将类型信息传递到 runtime 但是方法的返回类型是 compile-time 构造,因此编译器错误。而是通过扩展方法考虑类型类解决方案(再次从 Luis 的建议中劫持)

sealed trait Key
final case class SomeKey(a: Int, b: String) extends Key
final case class SomeOtherKey(a: Int, b: String, c: Boolean) extends Key

trait MyTrait[T <: Key]

trait KeyFactory[T <: Key] {
  def someFunc(): Map[T, Int]
}

object KeyFactory {
  def someFunc[T <: Key](implicit ev: KeyFactory[T]) = ev.someFunc
  implicit val someKeyFoo: KeyFactory[SomeKey] = () => Map(SomeKey(1,"2") -> 1)
  implicit val someOtherKey: KeyFactory[SomeOtherKey] = () => Map(SomeOtherKey(1,"2", true) -> 2)
}

implicit final class MyTraitKeyFactory[T <: Key : KeyFactory](private val v: MyTrait[T]) {
  def someFunc(): Map[T, Int] = implicitly[KeyFactory[T]].someFunc()
}

case class MyClass(s: Int) extends MyTrait[SomeKey]
case class MyOtherClass(s: Int) extends MyTrait[SomeOtherKey]


MyOtherClass(42).someFunc()
MyClass(11).someFunc()

哪个输出

res0: Map[SomeOtherKey,Int] = Map(SomeOtherKey(1,2,true) -> 2)
res1: Map[SomeKey,Int] = Map(SomeKey(1,2) -> 1)

【讨论】:

    猜你喜欢
    • 2019-10-29
    • 2019-12-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多