17
sealed class A
class B1 extends A    
class B2 extends A

Assuming we have a List of objects of class A : val l: List[A] = List(new B1, new B2, new B1, new B1)

And we want to filter out the elements of the type B1. Then we need a predicate and could use the following two alternatives:

l.filter(_.isInstanceOf[B1])

Or

l.filter(_ match {case b: B1 => true; case _ => false})

Personally, I like the first approach more, but I often read, one should use the match-case statement more often (for reasons I do not know).

Therefore, the question is: Are there drawbacks of using isInstanceOf instead of the match-case statement ? When should one use which approach (and which approach should be used here and why) ?

John Threepwood
  • 15,593
  • 27
  • 93
  • 149

4 Answers4

20

You can filter like that:

l.collect{ case x: B1 => x }

That is much more readable, IMO.

om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
  • 3
    And gives you the narrower type at the end (`List[B1]`). – Rex Kerr Jun 27 '12 at 16:08
  • Looks much more readable, thank you. But what will be the default case in this statement ? Where can I read more background information about this shortcut case statement and how it is used? – John Threepwood Jun 27 '12 at 17:10
  • @JohnThreepwood take a look at [this post](http://ochafik.com/blog/?p=393). It is not the best, but still pretty much good "the why of collect". Shortly, collect is like map, but make a work only for those values that defined for *(partial)* function you pass to this method. Scala's pattern matching in this case is done on top of [PartialFunction](http://stackoverflow.com/questions/5668053/scala-partial-functions). Also, you need to use curly brackets. – om-nom-nom Jun 27 '12 at 17:31
  • @om-nom-nom Thank you for the additional information and the link, that helped a lot ! – John Threepwood Jun 28 '12 at 08:05
14

There's no problem using isInstanceOf, as long as you don't use asInstanceOf.

Code that uses both is brittle, because checking and casting are separate actions, whereas using matching you have a single action doing both.

Daniel C. Sobral
  • 295,120
  • 86
  • 501
  • 681
13

There are no difference

cat t.scala:

class A {
  def x(o: AnyRef) = o.isInstanceOf[A]
  def y(o: AnyRef) = o match {
    case s: A => true
    case _ => false
  }
}

$ scalac -print t.scala

[[syntax trees at end of cleanup]]// Scala source: t.scala
package <empty> {
  class A extends java.lang.Object with ScalaObject {
    def x(o: java.lang.Object): Boolean = o.$isInstanceOf[A]();
    def y(o: java.lang.Object): Boolean = {
      <synthetic> val temp1: java.lang.Object = o;
      temp1.$isInstanceOf[A]()
    };
    def this(): A = {
      A.super.this();
      ()
    }
  }
}
Zang MingJie
  • 5,164
  • 1
  • 14
  • 27
12

The advantage of match-case is that you don't have to cast the object in case you want to perform operations on it that depend on its narrower type.

In the following snippet, using isInstanceOf seems to be fine since you don't perform such an operation:

if (obj.isInstanceOf[A]) println(obj)

However, if you do the following:

if (obj.isInstanceOf[A]) {
  val a = obj.asInstanceOf[A]
  println(a.someField) // someField is declared by A
}

then I'd be in favour of using match-case:

obj match {
  case a: A => println(a.someField)
  case _ =>
}

It is slightly annoying that you have to include the "otherwise"-case, but using collect (as hinted at by om-nom-nom) could help, at least if you work with collections inherit from Seq:

collectionOfObj.collect{ case a: A => a}.foreach(println(_.someField))
om-nom-nom
  • 62,329
  • 13
  • 183
  • 228
Malte Schwerhoff
  • 12,684
  • 4
  • 41
  • 71