如何在Scala中使用抽象类型成员

时间:2017-07-06 01:06:00

标签: scala generics

我遇到了以下代码的问题(大大简化)。看起来问题可能与我使用抽象类型成员的方式有关。我很感激有人指出并解释我在这里做错了什么。编译器错误在底部。 我正在使用Scala 2.12版。

    $('#Year option')
     .removeAttr('selected')
     .filter('[value=@Model.Birthday.Year]')
         .attr('selected', true)

编译错误。

trait DataType {
    type A

    def convert(value: String): A
    def convertToString(value: A): String
}

case object IntType extends DataType {
    type A = Int

    def convert(value: String): A = value.toInt
    def convertToString(value: A): String = value.toString
}

trait Codec[T <: DataType] {
    val dtype: T

    def encode(data: Array[String]): Array[T#A]
    def decode(data: Array[T#A]): Array[String]
}

class CodecImp[T <: DataType](val dtype: T)(implicit tag: ClassTag[T#A]) extends Codec[T] {
    def encode(data: Array[String]): Array[T#A] = {
        Array[T#A](dtype.convert(data(0)))
    }

    def decode(data: Array[T#A]): Array[String] = {
        Array[String](dtype.convertToString(data(0)))
    }
}

val cod = new CodecImp(IntType)
val encoded = cod.encode(Array("1", "2", "3")) // expecting: Array[IntType.A]
val decoded = cod.decode(encoded) // expecting: Array[String]

3 个答案:

答案 0 :(得分:2)

我发现What does the # operator mean in Scala?很好地解释了'#'运算符 DataType的每个实例都有自己的路径依赖类型A.
区别为:T#A意味着A是任何T的嵌套类,dtype.A意味着A类的dtype

您可以将Codec特征方法签名更改为:

def encode(data: Array[String]): Array[dtype.A]
def decode(data: Array[dtype.A]): Array[String]

但是类型参数可能是表达关系的更好方式。

答案 1 :(得分:1)

这是由dtype.convertToString(data(0))此方法需要类型为dtype#A变量引起的,此类型由变量dtype决定,但是{data 1}}类型为Array[T#A],因此导致类型不匹配,它无法在 Scala 中解析,因为我们可以&#39 ; t陈述我们的方法,例如:def decode(data: Array[dType.A])...预期的编译器

您可以通过泛型类型再次类型别名解决此问题,例如:

trait DataType[T] {
  def convert(value: String): T
  def convertToString(value: T): String
}

case object IntType extends DataType[Int] {

  def convert(value: String): Int = value.toInt
  def convertToString(value: Int): String = value.toString
}


class CodecImp[B](val dtype: DataType[B])(implicit tag: ClassTag[B]) {
  def encode(data: Array[String]): Array[B] = {
    Array[B](dtype.convert(data(0)))
  }

  def decode(data: Array[B]): Array[String] = {
    Array[String](dtype.convertToString(data(0)))
  }
}

答案 2 :(得分:0)

问题在于DataType的每个实例都可以将A定义为任何内容。考虑一下:

class Foo extends DataType {
   type A = Int
   def convertToString(i: Int) = i.toString
   def convert(s: String) = s.toInt
}

class Bar extends DataType {
    type A = String
    def convertToString(s: String) = s
    def convert(s: String) = s
 }

现在,如果我做val codecs = Seq(CodecImpl(new Foo, new Bar)

如果编译,那么参数应该是codecs.map(_.decode(whateverMakesSenseHere))的哪种类型?

这不起作用......你不能像这样使用T#A,因为它是抽象的。

我有一种感觉,你最好不要使用类型参数而不是路径依赖类型来建模你需要建模的东西。对于后者来说,这似乎并不像是一个用例。