类型参数限制为 final class 的模式匹配

Pattern matching with type parameter bounded to final class

这是一个例子

def maybeeq[A <: String](x: A):A = x match {
  case z:A => x
}

编译时出现如下错误信息

Error:(27, 12) scrutinee is incompatible with pattern type; found : A required: String case z:A => x

我可以将任何最终 class 放入 A 的绑定中以重现错误。

为什么这对非最终 classes 编译但对最终失败?为什么类型擦除不只是将 A 替换为 String?

已编辑:

注意:这样的绑定允许我将字符串类型的值传递给 'x' 参数。所以 'x' 可以只是一个字符串,不必是字符串的子类型,所以我不要求编译器编译带有不正确签名的方法。在现实世界的代码中,我只是将 String 放在 A 参数上,但从实验的角度来看,我很感兴趣为什么需要在现有限制之上进行这种额外限制(基于最终 class 性质)。

Final class 不能是 extended.so 因为 def maybeeq[A <: String](x: A) 不是正确的语法,因为 Stringfinal,不应有任何 子类型 String 扩展。编译器巧妙地指出了这个问题。

TBH 这是一个关于编译器设计的问题,只能由实施此类检查的人回答

a test in compiler test suite that requires such error to be shown。它与类型信息被丢弃到无法将具体类型分配给变量的程度有关,但从该测试的 git blame 无法理解其原因。


不过,我要指出的是,仍然有许多方法可以满足 A <: String 而不会在编译时将 A 已知为 String。一方面,NullNothing 满足这一点,处于 Scala 类型层次结构的底部。这两个在类型匹配时被明确禁止。另一个例子有点复杂:

val UhOh: { type T <: String } = new { type T = String }
implicitly[UhOh.T <:< String] // satisfies type bound
implicitly[UhOh.T =:= String] // won't compile - compiler cannot prove the type equality

这类似于一些新的打字模式,例如shapeless.tag

在所有这些可能性中,只有当 A =:= String 时可以做任何合理的事情,因为 String 是唯一可以在运行时实际检查的类型。哦,除非你在匹配中使用泛型类型——那根本不起作用(至少在范围内没有 ClassTag),因为这些类型被擦除消除了。