本文介绍了协变类型参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

最近我一直在努力使我对Scala的理解更深入一点,我无法真正弄清楚有关协变/相反变量类型参数的某些事情。

I have been trying to push my understanding of Scala a little bit further lately and I cannot really figure out some things about covariant/contravariant type parameters.

我有一个名为 Basket 的类,如下所示:

Let's say I have a class called Basket as follows :

class Basket[+A <: Fruit](items: List[A]) {
  // ...
  def addAll[B >: A <: Fruit](newItems: List[B]): Basket[B] =
new Basket(items ++ newItems)
  // ...
}

以及一些类似这样的类:

and some classes like this:

trait Fruit
class Banana extends Fruit
class Orange extends Fruit






我知道确保这些断言是正确的:


I know for sure that these assertions are correct :


  • Basket [水果] 可以实例化

Basket [String] 无法实例化(因为 String 不是水果)的子类型

Basket[String] cannot be instantiated (because String is not a subtype of Fruit)

购物篮[巴南a] 购物篮[水果]

Basket [Orange] Basket [Fruit]

此代码:

val bananaBasket: Basket[Banana] = new Basket(List(new Banana, new Banana))
bananaBasket.addAll(List(new Orange))


将返回篮子[水果]


  • 此代码:

  • This code:

val bananaBasket: Basket[Banana] = new Basket(List(new Banana, new Banana))
bananaBasket.addAll(List(new Banana))


将返回一个 Basket [Banana]

什么我不明白 B> ;: A 如何影响方法的返回类型..为什么当我添加 Orange 返回类型变为 Basket [Fruit] ,当我添加 Banana 时,它保持为 Basket [Banana] 吗?它会寻找最低的公共超类型吗?

What I do not understand is how the B >: A affects the return type of the method .. Why when I add an Orange the return type become Basket[Fruit] and when I add a Banana, it stays a Basket[Banana] ? Does it look for the "lowest" common super-type ?

推荐答案

是的,Scala编译器尝试查找最低的公共超类型。 -类型。您可以在Scala的任何位置(包括标准库类)看到它。考虑List的这个示例,它的参数类型也是协变的:

Yes, the Scala compiler tries to find the lowest common super-type. You can see it anywhere in Scala, including standard library classes. Consider this example for List, which is also covariant on its parameter type:

1.0 :: List(1, 2, 3)
// result type is AnyVal, least common ancestor of Double and Int
res1: List[AnyVal] = List(1.0, 1, 2, 3)

"0" :: List(1, 2, 3)
// result type is List[Any], lowest common ancestor of String and Int
res2: List[Any] = List(0, 1, 2, 3)

0 :: List(1, 2, 3)
// result type is List[Int] exactly
res3: List[Int] = List(0, 1, 2, 3)

res2.head
res4: Any = 0

res2.head.asInstanceOf[String]
res5: String = "0"

有人可以说这是Scala类型系统的可疑特征,因为它很容易结束用诸如 Any (在我的示例中是这样)或具有串行化的产品 (如果要处理大小写)类),然后错误消息会引起误解。

One can argue this is a dubious feature of Scala type system, because it's easy to end up with something like Any (how it's in my example) or Product with Serializable (if you're dealing with case classes), and then the error messages are quite misleading.

如果您想限制Scala推广您的类型,则应使用悲伤的帽子类型约束<:< 。然后您的代码将如下所示(为了方便阅读,我将类更改为case类):

If you want to restrict Scala from generalizing your types, you should use the "sad in a hat" type constraint <:<. Then your code would look like this (I've changed the classes to case classes for readability):

case class Banana() extends Fruit
defined class Banana

case class Orange() extends Fruit
defined class Orange

case class Basket[+A <: Fruit](items: List[A]) {
    // ...
    def addAll[B >: A <: Fruit, C >: A](newItems: List[B])(implicit ev: B <:< C): Basket[B] =
  new Basket(items ++ newItems)
    // ...
  }
defined class Basket

val bananaBasket: Basket[Banana] = Basket(List(Banana(), Banana()))
bananaBasket: Basket[Banana] = Basket(List(Banana(), Banana()))

bananaBasket.addAll(List(Orange())) // not accepted
Main.scala:593: Cannot prove that Product with Serializable with cmd27.Fruit <:< cmd47.Banana.
bananaBasket.addAll(List(Orange()))
                   ^
Compilation Failed

bananaBasket.addAll(List(Banana())) // accepted
res52: Basket[Banana] = Basket(List(Banana(), Banana(), Banana()))

您可以在以下非常有用的博客文章中了解有关此模式的更多信息:

You can read more about this pattern in an very informative blog post here: http://blog.bruchez.name/2015/11/generalized-type-constraints-in-scala.html

这篇关于协变类型参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-14 04:49