【问题标题】:scala lower and upper boundsscala 下界和上界
【发布时间】:2016-10-14 19:56:07
【问题描述】:
class Queue[+T](
    private val leading: List[T],
    private val trailing: List[T]
) {
    def append[U >: T](x: U) =
      new Queue[U](leading, x :: trailing) // ...
}

class Fruit

class oranges extends Fruit

class apple extends Fruit

class diffAppale

val q1: Queue[Fruit] = new Queue[apple](List(new apple), List())
//> q1  : Test.Queue[Test.Fruit] = Test$Queue@30c7da1e

q1.append(new Fruit)                            
//> res0: Test.Queue[Test.Fruit] = Test$Queue@506e6d5e

q1.append(new oranges)                          
//> res1: Test.Queue[Test.Fruit] = Test$Queue@96532d6

q1.append(new diffAppale)  // i want to restrict this             
//> res2: Test.Queue[Object] = Test$Queue@3796751b

在这里我可以添加任何对象我可以添加到附加函数,我可以看到结果类型被降级为最低公分母

但我想拥有与 java 相同的行为,比如 def append[? super T](x: U) // 这里的 append 函数将获取所有属于 T 超类型的对象,我如何在 scala 中实现类似的对象(实现 super 并为 java 等泛型扩展)

【问题讨论】:

  • 为什么这对你来说是个问题?
  • 我想只通过append函数添加一些特定的对象类型,可以通过>:,
  • 我认为您正在尝试考虑更复杂的需要。如果您在特定情况下所需的返回类型是Queue[Fruit],无论如何它都不允许您返回Queue[Any]。大多数时候,当程序正确地进行类型检查时,人为地强制执行额外的类型约束是没有意义的。无论如何,正如我在答案中建议的那样,如果你真的想要这个,只需删除差异。

标签: scala generics


【解决方案1】:

我不明白您为什么要限制这一点,或者为什么返回更通用的新不可变对象会给您带来问题。

但如果您不想要这种差异,只需删除所有差异注释:

class Queue[A](leading: List[A], trailing: List[A]) {
  def append(x: A) = new Queue[A](leading, x :: trailing) // ...
}

class Fruit
class Orange extends Fruit
class Apple  extends Fruit
class NotFruit

val q1: Queue[Fruit] = new Queue(List(new Apple), Nil)
val q2 = q1.append(new Fruit)  // ok
val q3 = q2.append(new Orange) // ok

q1.append(new NotFruit) // error - found NotFruit, required Fruit

关于问题:

append 函数将获取所有属于 T 超类型的对象

这是您的原始代码已经完成的工作。请注意,Any 是所有类型的超类型。由于参数处于协变位置,因此始终可以传递子类型为 Any 的值对于预期类型的​​参数 Any。这就是生活 :) Scala 只是感觉与 Java 不同,因为它是围绕声明站点差异而不使用站点差异构建的(可以说是比 Java 的泛型更好的决定)。

如果您想要进一步的约束,您可以要求提供证据参数,例如

def append[B >: A](x: B)(implicit ev: B <:< Seedless): Queue[B]

或者设置一个上限(这不能与A 相同,因为A 的差异):

def append[B >: A <: Fruit](x: B): Queue[B]

但是:恕我直言,这真的没有意义。

【讨论】:

  • 现在我有点困惑:),你已经删除了 A(+A) 的覆盖,但你仍然可以将 Queue[apples] 添加到 Queue[Fruit] 如下
  • val q1: Queue[Fruit] = new Queue(List(new Apple), Nil) 但是如果删除了覆盖,我无法像下面这样添加 val q1: Queue[Fruit] = new Queue[apple ](List(new apple), List()),有什么区别!!!
  • @sharathchandra Scala(如 Java)是一种具有子类型的语言。 A 类型的参数始终可以通过传递B &lt;: A 类型的值来满足。您不需要为此进行方差注释,可以说这是“内置”功能。所以如果你有一个Queue[A]append(x: A) 方法,你总是可以用y: B 的值调用那个方法,如果B &lt;: A。从定义上讲这是合理的,因为该方法将y 视为A 类型的值并且可以安全地处理它,因为B 具有A 具有的所有功能(成员)。
  • 您不能在没有方差注释的情况下编写val q1: Queue[Fruit] = new Queue[Apple](List(new Apple), Nil),因为现在Queue[Apple] 不再是Queue[Fruit] 的子类型。你可以构造一个val q1: Queue[Fruit] = new Queue[Fruit](List(new Apple), Nil),因为List[+A]在它的元素类型上是协变的,所以一个List[Apple] &lt;: List[Fruit]。 Scala 将通过这种方式正确识别预期的返回类型Queue[Fruit]
  • 根据经验,如果您的类型参数的使用允许,请使用方差注释。 Scala 是一种具有子类型和支持不可变数据类型的语言,使用方差注释使其使用顺畅。不要担心返回类型可能会“扩大”,例如到Queue[Any],因为在下一步你会看到你可能不会对这种类型做任何事情。
猜你喜欢
  • 2017-02-01
  • 2018-03-19
  • 1970-01-01
  • 2023-03-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-10-13
相关资源
最近更新 更多