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!
Большое спасибо!