Scala - Как мне сделать метод "assertThrows"?

Я начал изучать Scala вчера, так что я довольно новичок в этом. Одна вещь, которую я люблю делать при изучении нового языка, это попытаться создать библиотеку микро-TDD.

Это то, что я получил так далеко:

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] )
}

Код для assert работает просто отлично, но у меня две проблемы с assertThrows,

  • Кажется, я не могу использовать E на последней строке. Независимо от того, что я делаю, я получаю class type expected but E found error,
  • Если я удалю E из последней строки (заменив его на throw new AssertionError("error expected")например) я получаю это: warning: abstract type E in type pattern is unchecked since it is eliminated by erasure

Я думаю, что две проблемы, с которыми я сталкиваюсь, связаны с тем, как Scala (и, вероятно, java) работает с абстрактными типами, и как они это делают.

Как я могу исправить мои assertThrows?

Бонусные баллы: это способ, которым я указываю "тип блока" (f: => Unit) правильный?

2 ответа

Решение

Виртуальная машина Java реализует обобщения через стирание типов, поэтому внутри тела метода JVM фактически ничего не знает о том, что такое тип E, поэтому этот метод AssertThrows не может работать так, как вам бы хотелось. Вам необходимо неявно передать Manifest для вашего класса исключений, например так:

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

Затем вы можете использовать его в теле, чтобы перехватить исключение или получить имя класса следующим образом:

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

Спасибо классу AssertThrows платформы Spring за то, что показал мне, как это сделать.

Я получил эту работу, благодаря ответу Кена:

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!

Большое спасибо!

Другие вопросы по тегам