代码之家  ›  专栏  ›  技术社区  ›  kmh

Scala多态性-协变和类型界

  •  1
  • kmh  · 技术社区  · 8 年前

    正在使用Scala。。。我不知道如何以混合类型绑定和协方差的方式使用多态性。

    简而言之,我 认为 我需要这种类型的签名。。。但如果你跟着我的假例子,你就会明白我为什么会到这里来。。。也许我错了。

    def func[+T <: U](func: Seq[T] => T)(iter: Iterator[String]): Map[String, String] = ???
    

    但这种方法会产生。。。

    >> error: ']' expected but identifier found
    

    这里有一个虚拟的例子,演示了我正在尝试做什么。。。我可以通过只使用基本特征记录来避免这个问题。。。但我想让它与实际代码中出于其他原因而嵌入的多态性一起工作。

    设置

    // underlying trait to hold key and value
    trait Record {
      def k: String 
      def v: String
      def isDefined: Boolean
    }
    
    // companion object with apply method
    object Record {
      def apply(s: String): Record = s.split(",") match {
        case Array(k,v) => new ValidRecord(k,v).asInstanceOf[Record]
        case _          => EmptyRecord.asInstanceOf[Record]
      }
    }
    
    // singleton for empty records
    object EmptyRecord extends Record {
      val k = ""
      val v = ""
      val isDefined = false
    }
    
    // class for actual data
    class ValidRecord(val k: String, val v: String) extends Record {
      val isDefined = true
    }
    

    多态函数

    注意-此处从迭代器转到Seq看起来有问题。。。我正在从src/main/resources读取一个文件。。。它以迭代器的形式出现。。。我最终需要把它放到地图上,所以。toSeq和。groupBy似乎是合乎逻辑的步骤。。。大概只有100MB和一百万条左右的记录,所以这很好用。。。但如果有一种更聪明的方式从开始到结束,我也愿意接受这种批评。

    def iter_2_map[T <: Record](func: Seq[T] => T)(iter: Iterator[String]): Map[String, String] = {
      iter                               // iterator of raw data
      .map(Record.apply)                 // Iterator[Record]
      .toSeq                             // gives .groupBy() method
      .groupBy(_.k)                      // Map[k -> Seq[Record]]; one Seq of records per k
      .mapValues(func) // <<< ERROR HERE //function to reduce Seq[Record] to 1 Record
      .filter(_._2.isDefined)            // get rid of empty results
      .mapValues(_.v)                    // target of Map is just v
    }
    

    错误

    found   : Seq[T] => T
    required: Seq[Record] => ?
              .mapValues(func)
                         ^
    

    如果我分解所有这些步骤并在每个相关步骤中声明类型。。。此错误更改为。。。

    found   : Seq[T] => T
    required: Seq[Record] => Record
              .mapValues(func)
                         ^
    

    这就是我被难住的地方。我认为让T协变可以解决这个问题。。。T是已声明的记录子类型,但它可能无法将Seq[T]识别为<:Seq[记录]?

    但是做这个改变会产生顶部的错误。。。

    def iter_2_map[+T <% Record](func: Seq[T] => T)(iter: Iterator[String]): Map[String, String] = {
      ???
    }
    

    回到这里。。。

    >>错误:“]”,但找到标识符
    

    我走对了吗?

    1 回复  |  直到 8 年前
        1
  •  2
  •   Dima    8 年前

    您正在使用 + 不正确。它仅用于的类型参数 类别 表示类的参数应该是协变的。 将其与方法一起使用没有多大意义( Seq[T] 事实上 的子类 Seq[Record] -因为 Seq 是协变的,但这对你没有帮助,因为 功能 逆变的 在他们的参数类型中 Function[Seq[T], T] 是一个 超类 属于 Function[Seq[Record], T] ,而不是子类)。原因如下:

    之后 .groupBy(_.k) 你有 Map[String, Seq[Record]] . 现在,你正在做 .mapValues(func) 并试图向其传递一个函数 序号【T】 . 这是行不通的。

    想象一下 Record Animal T Dog ... 和 func makeBark ... 现在你想把一群动物传给它,其中一些是 Cat s、 一些 Bird s、 也许还有一些 Fish . 你不能让他们都叫,对吗?

    您可以声明reducer函数来接受 记录 顺序而不是 T :

       def iter_2_map[T <: Record](func: Seq[Record] => T)(iter: Iterator[String])
    

    这将被编译,但它似乎对您没有多大用处,因为您似乎 预料 你的 func公司 能够返回 二者都 EmptyRecord ValidRecord ,而不仅仅是 T (因为您正在过滤之后的清空)。因此,实际上似乎您根本不需要type参数:

      def iter_2_map(func: Seq[Record] => Record)(iter: Iterator[String])