在调试我的程序时,我发现我错误地为containsSlice
的{{1}}方法提供了错误的参数。我提供的Seq[Char]
当然从未包含在Seq[Boolean]
。
编译器为什么让我这样做?这种方法不能在Scala集合库中以类型安全的方式实现吗?我认为必须刻意做到能够提供Seq[Char]
,理论上可以是Seq[Any]
。但设计师是否应该保持此方法的类型安全,并添加一些非类型安全的方法,如Char
?
修改:
我尝试在集合类层次结构之外创建自己的类型安全containsAnySlice
方法,但由于某种原因,containsSlice
方法中的代码编译,而我希望它不会:
test
我想这是因为object Seqs {
def containsSlice[A, B <: A](s1: Seq[A], s2: Seq[B]): Boolean = {
s1.containsSlice(s2)
}
def test = {
val sc: Seq[Char] = Seq('A','B','C')
val sb: Seq[Boolean] = Seq(true, false)
containsSlice(sc, sb)
}
}
和Char
(Boolean
)的常见超类型是针对类型参数AnyVal
推断的,我需要显式调用{{1如果我不想要那个(但我可以使用A
)。
为了使它不能为不兼容的containsSlice[Char](sc, sb)
编译,我能做到这一点的唯一方法是使用2个参数列表,以便推断出正确的类型:
sc.containsSlice[Char](sb)
答案 0 :(得分:3)
问题是Seq在其类型参数中被声明为covaraint:trait Seq[+A]
这意味着如果Seq[A]
是Seq[B]
的子类,A
是B
的子类。 containsSlice
。 trait MySeq[+A] {
def containsSlice(as: Seq[A]): Boolean
}
的参数自然处于反变量位置,这意味着它不能与类型参数A匹配,因为A已经被声明为协变。试着自己写一下:
error: covariant type A occurs in contravariant position in type Seq[A] of value as
def containsSlice(as: Seq[A]): Boolean
^
one error found
编译器会抱怨:
Seq[Char]
如果你玩这个,你怎么可能无论如何都不能保证类型安全。让我们假装编译器允许这个定义。在您的示例中,您有一个val myseq: Seq[Char] = List('a','b','c')
val anyseq: Seq[Any] = myseq
。因为Seq的协方差,这应该是允许的:
Seq[Char]
我们被允许这样做是因为Seq[Any]
是Char
的子类型,因为Any
是containsSlice
的子类型。现在,anyseq上Seq[Any]
的类型是什么?由于Seq
中anyseq
的类型参数为Any
,因此{{1}}为{{1}},因此您的类型安全性已消失。
答案 1 :(得分:3)
containsSlice
只是对indexOfSlice
的调用,具有以下签名:
indexOfSlice[B >: A](that: GenSeq[B]): Int
containsSlice
可以做同样的事情。
使用下限来约束类型参数是很有用的,所以你可以这样做:
apm@mara:~/tmp$ scalam -Ywarn-infer-any
Welcome to Scala version 2.11.0-M7 (OpenJDK 64-Bit Server VM, Java 1.7.0_25).
Type in expressions to have them evaluated.
Type :help for more information.
scala> "abcdef" contains 8
<console>:8: warning: a type was inferred to be `AnyVal`; this may indicate a programming error.
"abcdef" contains 8
^
res0: Boolean = false
警告告诉您类型安全性已受到损害。
-Xlint
包括-Ywarn-infer-any
。
但请查看:
scala> "abcdef" indexOfSlice (1 to 10)
res1: Int = -1
没有警告?
您可以启用花哨的新-Ytyper-debug
来查看:
| | | | | \-> [B >: Char](that: scala.collection.GenSeq[B], from: Int)Int <and> [B >: Char](that: scala.collection.GenSeq[B])Int
| | | | solving for (B: ?B)
和
| | | | solving for (B: ?B)
| | | | \-> Int
这似乎意味着在存在重载的情况下,您不会收到警告。
这是因为如何对重载进行类型检查。
-Xprint:typer
确认,当然,你结束AnyVal
,但必须在以后发生:
private[this] val res0: Int = scala.this.Predef.augmentString("abcdef").indexOfSlice[AnyVal](scala.this.Predef.intWrapper(1).to(10));
我们真的不需要另一个原因,即重载是邪恶的,但你知道println
也是邪恶的吗?
scala> println("abcdef" contains 8)
false
没有警告。
事实证明警告非常保守:
// Can warn about inferring Any/AnyVal as long as they don't appear
// explicitly anywhere amongst the formal, argument, result, or expected type.
由于println
需要Any
,这是我们预期的表达式类型,我们会失去警告。
总之,
import reflect.runtime._
import universe._
case class Foo[+A](a: A) {
def f[B >: A : TypeTag](other: Foo[B]): String = typeTag[B].toString
def g[B] = "huh"
def j[B >: A : TypeTag](other: Foo[B]): String = typeTag[B].toString
def j[B >: A : TypeTag](other: Foo[B], dummy: Int): String = typeTag[B].toString
}
object Test extends App {
val x = Foo("abc") f Foo(3) // warn
val y = Foo("abc").g
val w = Foo("abc") j Foo(3) // nowarn
val z = Foo("abc") j (Foo(3), 0)
Console println s"$x $y $w $z"
val b = "abcdef" contains 8 // warn
println("abcdef" contains 8) // nowarn
implicit class Sectional[+A](val as: Seq[A]) {
def containsSection[B >: A](other: Seq[B]): Boolean = as containsSlice other
}
("abcdefg": Seq[Char]) containsSection (1 to 10) // warn
implicit class Sectional2(val as: String) {
def containsSection[B >: String](other: Seq[B]): Boolean = as containsSlice other
}
"abcdefg" containsSection (1 to 10) // warn
}
特殊套管字符串太糟糕了。