首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >创建单个对象类型的列表

创建单个对象类型的列表
EN

Stack Overflow用户
提问于 2021-05-31 12:40:12
回答 1查看 122关注 0票数 4

我有一个Animal特性和一些案例类,如下所示

代码语言:javascript
复制
sealed trait Animal

trait Domestic extends Animal
trait Wild extends Animal

case class Dog(id: UUID = UUID.randomUUID()) extends Domestic
case class Lion(id: UUID = UUID.randomUUID()) extends Wild

这里是我的羊群类,它可以包含一种类型的动物的列表。

代码语言:javascript
复制
case class Herd[T <: Animal](get: T*)

我想创造一群单一类型的动物。

代码语言:javascript
复制
val herd1 = Herd(Cat(), Cat())
val herd2 = Herd(Cat(), Lion())

在Scala中两者都是有效的,但是如果您看一群猫和狮子的含义,这是没有意义的。是否有办法将Herd限制为单一类型?

EN

回答 1

Stack Overflow用户

回答已采纳

发布于 2021-05-31 13:11:56

尝试引入两个类型参数AB,然后将它们与一个通用类型约束A =:= B关联起来。

代码语言:javascript
复制
case class Herd[A <: Animal, B](x: A, y: B*)(implicit ev: A =:= B)

Herd(Lion())         // ok
Herd(Lion(), Lion()) // ok
Herd(Cat(), Lion())  // compile-time error

=:=到底是什么

考虑具有两个类型参数AB的以下方法,我们的目的是表示它们应该相等,或者至少A应该是B的一个子类型。

代码语言:javascript
复制
scala> def f[A, B](a: A, b: B): B = {
     |   val x: B = a
     |   x
     | }
         val x: B = a
                    ^
On line 2: error: type mismatch;
        found   : a.type (with underlying type A)
        required: B

在上述定义中,这两种类型参数是完全不相关的,且方法主体不影响类型参数的类型推断,因而产生了误差。现在,让我们尝试将它们与类型绑定的A <: B关联起来

代码语言:javascript
复制
scala> def f[A <: B, B](a: A, b: B): B = {
     |   val x: B = a
     |   x
     | }
def f[A <: B, B](a: A, b: B): B

因此,这将编译,但是编译器总是试图通过计算给定参数的最小上限来满足类型界限。

代码语言:javascript
复制
scala> f(Lion(), Dog())
val res32: Product with Animal with java.io.Serializable = Lion(...)

我们需要更多的东西来绕过编译器推导出最小上限的倾向,这就是泛化类型相等约束发挥作用的地方。

代码语言:javascript
复制
scala> def f[A <: B, B](a: A, b: B)(implicit ev: A =:= B): B = {
     |   val x: B = a
     |   x
     | }
def f[A <: B, B](a: A, b: B)(implicit ev: A =:= B): B

scala> f(Lion(), Cat())
        ^
       error: Cannot prove that Lion =:= Product with Animal with java.io.Serializable.

现在编译器仍然必须尝试生成给定参数的最小上限,但是它还必须满足能够生成两种类型的ev ( AB相等)的附加要求。(注意,如果可能,见证ev将由编译器自动实例化。)

一旦我们有了见证ev,我们就可以通过它的apply方法在AB类型之间自由移动,例如,考虑

代码语言:javascript
复制
scala> type A = Lion
type A

scala> type B = Lion
type B

scala> val a: A = Lion()
val a: A = Lion(...)

scala> val ev: =:=[A, B] = implicitly[A =:= B]
val ev: A =:= B = generalized constraint

scala> ev.apply(a)
val res44: B = Lion(...)

注意ev.apply(a)如何类型为B。我们可以这样应用=:=的原因是因为它实际上是一个函数。

代码语言:javascript
复制
scala> implicitly[(A =:= B) <:< Function1[A, B]]
val res43: A =:= B <:< A => B = generalized constraint

所以隐式参数列表

代码语言:javascript
复制
(implicit ev: A =:= B)

实际上是指定隐式转换函数。

代码语言:javascript
复制
(implicit ev: A => B)

因此,现在编译器能够在需要的地方自动注入隐式转换,如下所示

代码语言:javascript
复制
def f[A <: B, B](a: A, b: B)(implicit ev: A =:= B): B = {
  val x: B = a
  x
}

自动展开为

代码语言:javascript
复制
def f[A <: B, B](a: A, b: B)(implicit ev: A =:= B): B = {
  val x: B = ev.apply(a)
  x
}

总之,与类型界限一样,泛化的类型约束也是要求编译器在编译时对代码基进行进一步检查的另一种方式。

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/67773938

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档