I need to perform the same operation if an object passed to a method is either of acceptable types. Is it possible to eliminate the second case in the example below?
obj match {
case obj: String => print(obj)
case obj: Int => print(obj)
}
I need to perform the same operation if an object passed to a method is either of acceptable types. Is it possible to eliminate the second case in the example below?
obj match {
case obj: String => print(obj)
case obj: Int => print(obj)
}
Yes, it is possible, but not in every case. You can use Scalas |
operator in your specific example:
scala> ("hello": Any) match { case s @ (_: String | _: Int) => println(s)}
hello
scala> ("1": Any) match { case s @ (_: String | _: Int) => println(s)}
1
But this specific example only works because println
expects an argument of type Any
, which is in fact what you have here.
Consider this example:
scala> object X {def f(i: Int) = i.toString; def f(s: String) = s}
defined object X
scala> ("": Any) match { case s @ (_: String | _: Int) => X.f(s)}
<console>:9: error: overloaded method value f with alternatives:
(s: String)String <and>
(i: Int)String
cannot be applied to (Any)
("": Any) match { case s @ (_: String | _: Int) => X.f(s)}
^
The problem is that while there are two methods which could handle both types, the compiler can't decide which one should be called. This is because the compiler will infer type Any
for s
because it can be both String
or Int
and the compiler needs to chose their common supertype to fulfill this contract. And because there exists no method f
which takes an Any
you get a compilation error. In this case you need to separate the pattern:
scala> ("hello": Any) match { case s: String => X.f(s) case i: Int => X.f(i)}
res6: String = hello
In summary you should only combine multiple cases when they have the same type, if they don't you have to live with Any
. Though, I suggest not to live with Any
it is too easy to introduce typing bugs when the code changes.