8

I started learning Scala yesterday, so I'm pretty new to it. One thing I like to do when learning a new language is trying to create a micro-TDD lib.

This is what I got so far:

def assert(condition: Boolean, message: String) {
  if(!condition){ throw new AssertionError(message) }
}

def assertThrows[E](f: => Unit) {
  try {
    f
  } catch {
    case e: E => { return }
    case _: Exception => { }
  }
  throw new AssertionError("Expected error of type " + classOf[E] )
}

The code for assert works just fine, but I'm having two problems with assertThrows.

  • It seems that I can't use E on the last line. No matter what I do, I get a class type expected but E found error.
  • If I remove E from the last line (replacing it by throw new AssertionError("error expected"), for example) I get this: warning: abstract type E in type pattern is unchecked since it is eliminated by erasure

I think that the two problems I'm having are related with the way Scala (and probably java) deals with abstract types, and how are they done.

How can I fix my assertThrows?

Bonus points: is the way I'm specifying a "block type" (f: => Unit) correct ?

kikito
  • 51,734
  • 32
  • 149
  • 189

2 Answers2

9

The Java virtual machine implements generics through type erasure, so inside the method body the JVM doesn't actually know anything about what type E is, so this AssertThrows method can't work the way you'd like. You need to implicitly pass a Manifest for your exception class, like so:

def assertThrows[E](f: => Unit)(implicit eType:ClassManifest[E]) {

Then you can use it in the body to catch the exception or get the class name like so:

  try {
    f
  } catch {
    case e: Exception =>
      if ( eType.erasure.isAssignableFrom(e.getClass))
        return;
  }
  throw new AssertionError("Expected error of type " + eType.erasure.getName )
}

Thanks to the Spring framework's AssertThrows class for showing me how to do this.

Arturo Herrero
  • 12,772
  • 11
  • 42
  • 73
Ken Bloom
  • 57,498
  • 14
  • 111
  • 168
  • Thanks for your answer. Can you include an example of use? I'm not able to get it working. – kikito Oct 23 '11 at 14:13
  • Nevermind, I got it working (added one answer). I'm editing a small typo in your answer and marking it as correct. – kikito Oct 23 '11 at 14:33
1

I got this thing working thanks to Ken's answer:

class AssertException(msg: String) extends Exception(msg: String)

def assertThrows[E](f: => Unit)(implicit eType:ClassManifest[E]) {
  try{
    f
  } catch {
  case e: Exception =>
    if(eType.erasure.isAssignableFrom(e.getClass)) { return }
  }
  throw new AssertException("Expected error of type " + eType.erasure.getName )
}

/* examples of usage */

assertThrows[NullPointerException]{ throw new NullPointerException() } // ok
assertThrows[AssertException] { assertThrows[Exception]{ } } // ok!

Thanks a lot!

kikito
  • 51,734
  • 32
  • 149
  • 189