Scala擦除类型在不同方法中的匹配和使用

时间:2014-10-03 16:57:10

标签: scala reflection match manifest erasure

我一直在寻找这个,即使使用Manifest和Reflect API,它仍然很难实现。

使用Manifest和Reflection,我可以将List [Any]与类(List [A])匹配,我也可以通过类型T获得匹配,就像在 http://daily-scala.blogspot.co.uk/2010/01/overcoming-type-erasure-in-matching-1.html

How save a TypeTag and then use it later to reattach the type to an Any (Scala 2.10)

但是如何确定输入的类型并在方法中使用它?

说,

object test {

val list : List[List[Any]] = List(
  List(2.5, 3.6 ,7.9),
  List("EUR","HKD", "USD")
)

def calculateString(in:List[String]) = {
  println("It's a String List")
  println(in)
}

  def calculateDouble(in:List[String]) = {
    println("It's a Double List")
    println(in)
  }

  def main( args: Array[String]){
    list.foreach(l=> matchAndCalculate(l))
  }


  // Copy from Andrzej Jozwik it doesn't work, but it's good to demonstrate the idea
  def matchAndCalculate(list:List[Any]) = list match {
    case i if i.headOption.exists(_.isInstanceOf[Long]) =>  calculateLong(i)
    case i if i.headOption.exists(_.isInstanceOf[String]) =>  calculateString(i)
  }

}

非常感谢

哈维

PS :正如Sarah指出的那样,在我将列表放入更复杂的结构之前,它可能是保持类型清单的唯一方法。

以下是挑战:是可以将List [Any]强制转换为/匹配到List [String]并作为def dummyMethod(stringList:List)等方法的输入[String])没有惹恼编译器?

2 个答案:

答案 0 :(得分:1)

    def calculateString(in:List[String]) = {
      println("It's a String List")
      println(in)
    }

    def calculateDouble(in:List[Double]){
     println("It's a Double List")
        println(in)
    }

    def castTo[T](t:T,list:List[Any]) = list.asInstanceOf[List[T]] 

   def matchAndCalculate(list:List[Any]) = list.headOption match {
        case Some(x:Double) => calculateDouble(castTo(x,list))
        case Some(x:String) => calculateString(castTo(x,list))         
        }

并检查:

scala> matchAndCalculate(List(3.4))
It's a Double List
List(3.4)

scala> matchAndCalculate(List("3.4"))
It's a String List
List(3.4)

scala> val list : List[List[Any]] = List(
     |   List(2.5, 3.6 ,7.9),
     |   List("EUR","HKD", "USD")
     | )
list: List[List[Any]] = List(List(2.5, 3.6, 7.9), List(EUR, HKD, USD))

scala> list.foreach(l=> matchAndCalculate(l))
It's a Double List
List(2.5, 3.6, 7.9)
It's a String List
List(EUR, HKD, USD)

答案 1 :(得分:1)

除非您可以更改数据结构,否则Andrej的解决方案是唯一合理的方法。

您无法真正使用类型清单,因为您有两个间接级别。您需要为外部列表的每个成员使用不同的类型清单。例如,您可以拥有List[(List[Any], TypeTag[Any])],但是除非您在“#{1}”时获取有关每个行的编译时信息,否则无法获取该信息。 ;重新构建列表。

如果您真的想要携带静态类型信息,那么使用implicits很容易做到这一点,只需将外部列表中的每个条目都设为特殊类。

一个简单的变体可能如下所示:

List

这种通用编程的另一个选择是使用Miles Sabin的Shapeless。这使用特殊的数据结构来构建任意大小的元组,可以将其视为类型安全列表。它生成一个类似于链表的数据结构,其中包含一个通用类型包装器,用于跟踪每一行的类型,因此除非您的列表很短,否则您不会想要使用它。它也有点难以学习,维护和理解 - 但是当你理解并正确使用它时,它会开启一些深刻的魔法。

我对您的用例了解不足以了解在这种情况下Shapeless是否可行。

在Scala 2.11的Shapeless中,解决方案看起来像这样:

class CalculableList[A](val elements: List[A])(implicit val calc: Calculator[A]) {
  def calculate = calc(elements)
}
trait Calculator[-A] extends (List[A] => Unit)
implicit object StringCalc extends Calculator[String] {
  def apply(in: List[String]) {
    println("It's a String List")
    println(in)
  }
}
implicit object DoubleCalc extends Calculator[Double] {
  def apply(in: List[Double]) {
    println("It's a Double List")
    println(in)
  }
}

val list: List[CalculableList[_]] = List(
  new CalculableList(List(1.0, 2.0, 3.0)),
  new CalculableList(List("a", "b", "c"))
)

list foreach { _.calculate }