可以用通配符表示的存在类型会导致不同的行为

问题描述

考虑以下示例:

sealed trait Test

case object A extends Test

sealed trait Type {
  type Tst <: Test
}

type Aux[T <: Test] = Type { type Tst = T }

case object Type1 extends Type {
  type Tst = A.type
}

case class Model[T <: Type](t: T)

val a: Model[Aux[A.type]] = null

val b: Model[_ <: Aux[_ <: Test]] = a //Compiles fine

val c: Model[T forSome { type T <: Aux[_ <: Test]}] = a  //Compile error type mismatch

Scastie Demo

我认为 val bval c 的类型是相同的。为什么后面的案例不编译?

解决方法

这里的技巧是提取参数列表之外的存在类型定义,如下所示

val c: Model[T] forSome { type T <: Aux[_ <: Test]} = a

val d: Model[T] forSome { type T <: Aux[T] forSome { type T <: Test}} = a

尽管这种行为非常不明显,但我通过一些奇怪的实验找到了解决方案。