代码之家  ›  专栏  ›  技术社区  ›  Mutating Algorithm

在具有协变类型的scala参数化类中实现方法

  •  2
  • Mutating Algorithm  · 技术社区  · 6 年前

    我已经阅读了一些教程,包括有关协变类型的方法签名的主要scala文档。假设我有以下抽象类:

    abstract class List[+A] {
    
      def head: A
      def tail: List[A]
      def isEmpty: Boolean
      def add[B >: A](element: B): List[B]
      protected def printElements: String
    
      override def toString: String = "[" + printElements + "]"
    
    }
    

    我的问题是 add() 方法。为什么有必要这样宣布?我们正在传入一个超类型A的参数。这能解决什么问题?我试图从直觉的角度来理解这一点。

    3 回复  |  直到 6 年前
        1
  •  3
  •   Luis Miguel Mejía Suárez    6 年前

    正式解释

    鉴于

    abstract class List[+A] {
      def add(element: A): List[A]
    }
    

    “此程序不编译,因为参数 要素 在里面 add 属于类型 A ,我们宣布 协变的 . 这不起作用,因为 功能 逆变 参数类型和 协变的 在它们的结果类型中。要解决这个问题,我们需要翻转 方差 参数类型的 要素 在里面 添加 .
    我们通过引入一个新的类型参数来实现这一点。 B 那有 作为一个 下类型界限 “。
    —— reference .

    直观的解释

    在这个例子中,如果 添加 某物 :
    一定是一个 -在这种情况下, 仍然是 List[A] .
    或者一定是 亚型 属于 -在这种情况下,元素 被抛弃的 仍然是 表[A] .
    或者如果它是另一种类型 ,那么它一定是 超类型 属于 -在这种情况下, 被提升到 List[B] . (注:因为 Any 只是一种超类型的东西,在最坏的情况下 将被提升到 List[Any] ) .

        2
  •  3
  •   Silvio Mayolo    6 年前

    假设我想要一个整数列表。为了争论,假设 add 在没有泛型的情况下实现。

    def add(element: A): List[A]
    

    为了这个例子,假设我们有某种方法来生成一个“空”列表。

    def emptyList[A]: List[A] = /* some magic */
    

    现在我想列出整数。

    (1 to 10).foldRight(emptyList) { (x, acc) => acc.add(x) }
    

    哎呀!我们有问题!当我呼唤 emptyList ,scala将推断 最普通的类型 以及以后 A 是协变的,假设 Nothing . 这意味着我只是想把一个整数添加到一个没有任何内容的列表中。我们 能够 使用显式类型签名解决此问题,

    (1 to 10).foldRight(emptyList[Int]) { (x, acc) => acc.add(x) }
    

    但实际上,这并不能解决问题。它不增加可读性,只需要用户做额外的工作。实际上,我应该能够在一个没有任何内容的列表中附加一个数字。只是,如果我选择这样做,我不能有意义地称之为 没有什么 不再。因此,如果我们定义

    def add[B >: A](element: B): List[B]
    

    现在,我可以从 List[Nothing] 并添加一个 Int 对它。我出去的不是 列出[没有] 这是一个 List[Int] 但我能做到。如果我接受 列表[ int ] 以后再来加一个 String 对它来说,我也可以这样做,但现在我有一个几乎无用的 List[Any] .

        3
  •  1
  •   Brian McCutchon    6 年前

    当你申报 +A 例如,你是说, List[String] 延伸 List[Object] . 现在,想象一下:

    val ls: List[Object] = List[String]() // Legal because of covariance
    ls.add(1) // Adding an int to a list of String?
    

    只有当列表的类型可以扩展为包含任意对象时,这才是合法的,这正是添加签名所做的。否则,存在 add(a: A) 这意味着类型系统中存在不一致。