简体   繁体   English

Scala泛型-为什么在使用类型约束时,scala返回超类型的实例而不是子类型的实例?

[英]Scala generics - Why scala returns an instance of supertype instead of the subtype when type constraint is used?

I'm trying to transform y into something which can be appended to x, where x is a sequence of some sorts. 我正在尝试将y转换为可以附加到x的东西,其中x是某种序列。

scala> def foo[U <: Seq[T], T](x: U, y: T): U = x :+ y
<console>:7: error: type mismatch;
 found   : Seq[T]
 required: U
       def foo[U <: Seq[T], T](x: U, y: T): U = x :+ y
                                                  ^

I have the following solutions: 我有以下解决方案:

def foo[T]( x : Seq[T], y:T) = x :+ y
def foo[T]( x : Seq[T], y:T) : Seq[T] = x :+ y
def foo[U <: Seq[T], T](x: U, y: T): U = (x :+ y).asInstanceOf[U]

But my doubt is why the original one didn't work. 但是我的疑问是为什么原始的那个没用。 It looks like if I apply an operator ( :+ in this case) defined in the super class then it returns the super class? 看起来如果我应用超类中定义的运算符(在本例中为:+ ),那么它将返回超类? ie if U is a Vector , foo returns Seq , so I'm getting error required "U" but found "Seq[T]" . 也就是说,如果U是一个Vector ,则foo返回Seq ,因此我得到了错误提示required "U" but found "Seq[T]"

Can anyone enlighten me why this behavior is seen? 谁能启发我为什么看到这种行为?

When coming across type problems, I usually adopt the "if it passes the compilation, what will happen" logic to find the unreasonable part. 当遇到类型问题时,我通常采用“如果通过编译,将会发生什么”的逻辑来查找不合理的部分。

In your case, assuming the original one is Okay. 在您的情况下,假设原始版本还可以。

 def foo[U <: Seq[T], T](x: U, y: T): U = x :+ y

cause Seq[T] is covariant on T, so the following case stands. 因为Seq [T]在T上是协变的,所以以下情况成立。

 for type A, T, if A <: T, List[A] <: Seq[T]

Then we can do the following operation: 然后,我们可以执行以下操作:

 class Parent 
 class Child extends Parent

 // List(new Child) :+ (new Parent) => List[Parent]
val result = foo(List(new Child), new Parent)

U is actually List[Child] in the foo method, but when List operates with a different type from its element type, it will try to find the common parent, in this case result is typed with List[Parent], but the required type is List[Child]. U实际上是foo方法中的List [Child],但是当List使用与其元素类型不同的类型进行操作时,它将尝试查找公共父级,在这种情况下,结果将使用List [Parent]键入,但是所需的类型是List [Child]。 Obviously, List[Parent] is not a subtype of List[Child]. 显然,List [Parent]不是List [Child]的子类型。

So, the thing is the final type is elevated but the required type is a subtype of the elevated type. 因此,事情是最终类型被提升,但是所需类型是提升类型的子类型。 If you look at the definition of Scala SeqLike, this may be clearer. 如果您查看Scala SeqLike的定义,这可能会更清楚。

trait SeqLike[+A, +Repr] extends ... {
    def :+[B >: A, That](elem: B)(...): That = {
       ...
    }
}

Lets simplify this example 让我们简化这个例子

  class T
  class B extends T

  def bar[U <: T](x: T): U = {
    new B
  }

This won't compile, cause when you call 这不会编译,因为当您调用

bar(new T)

you should return type T, but you're trying to return type B. B is subtype of T, but you should return exectly U, but not just a subtype if T. 您应该返回类型T,但是您尝试返回类型B。B是T的子类型,但是您应该返回U,而不仅仅是T的子类型。

You can fix your problem by 您可以通过以下方式解决问题

def foo[U <: Seq[T], T](x: U, y: T): Seq[T] = x :+ y

or 要么

def foo[B >: Seq[T], U <: Seq[T], T](x: U, y: T): B = y +: x

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM