Hi,
consider the code as follows:
scala> val sb = StringBuilder("abc")
val sb: StringBuilder = abc
scala> sb.contains('a')
val res0: Boolean = true
scala> sb.contains("ab")
val res1: Boolean = false
scala> sb.contains(1)
val res2: Boolean = false
I wonder why contains
with an argument of String
or Int
returns false
instead of compilation error? Since the contains is defined at SeqOps
(scala.StringBuilder
is derived from AbstractSeq[Char]
) as def contains[A1 >: A](elem: A1): Boolean = exists (_ == elem)
, and neither String
, nor Int
are supertypes of Char
.
The signature contains[A1 >: A](elem: A1)
does say, that A1
must be a supertype of Char
in this case. But the parameter elem
does not have to be that exact type, it may also be any subtype of A1
.
If you do not explicitly specify A1
, the compiler will infer it not to the type of elem
, but to the least common supertype of Char
and elem
's type. In case of Int
, this would be AnyVal
, for String
it would be Any
. As Any
can always be used as common supertype, you’ll never get a type error, without explicitly specifying A1
.
@ val sb = new StringBuilder("abc")
sb: StringBuilder = IndexedSeq('a', 'b', 'c')
@ sb.contains("a")
res11: Boolean = false
@ sb.contains[Any]("a")
res12: Boolean = false
@ sb.contains[String]("a")
cmd13.sc:1: type arguments [String] do not conform to method contains's type parameter bounds [A1 >: Char]
val res13 = sb.contains[String]("a")
^
Compilation Failed
Also note, that Int
is “kind of a supertype” for Char
in the sense that operations like comparisons and arithmetic containing both will work. Char
is an unsigned integer with lower range, so it can be converted to an Int
without losing information. So, if you pass the integer value of a character in the StringBuilder
, you can even get true
as result:
@ sb.contains(97) //97 is the ascii code point for 'a'
res16: Boolean = true
1 Like
See also this discussion about a more typesafe contains
method and why it is difficult.
One thing, that helps with most cases of unintended type-unsafeness, is using the WartRemover compiler plugin with the Any
, AnyVal
, Product
and JavaSerializable
warts enabled, which will cause the compiler to warn or error, if one of these types is inferred by the compiler.
2 Likes