Ich Bin Mblinner

Scala Custom Exceptions

At work, we’ve been doing a lot of our new stuff in Scala lately. One thing that we’re writing is a client, packaged up as a jar, that needs to work from both Scala and Java code. For the most part, the interop between Java and Scala is smooth, but there are occasional strange hiccups.

Scala hasn’t really given me much as much cause to define custom exceptions as I find in Java. Partially this is because we’re doing a lot of work in Akka, which has an Erlang like ‘let it fail’ model. Part of it has to do with Scala’s Option, I find myself returning a None in a lot of situations where I might have thrown an exception in Java.

So when I finally had to create a custom exception for Java interop, I ran into one a bit of a rough edge. Creating a simple custom exception class is, of course, a one-liner.

1
class ScalaCustomException(msg: String) extends RuntimeException(msg)

(Thanks Mishu, for noticing I forgot to put the superconstructor call here)

The problem comes in when you need two constructors, one that just takes a message, and one that takes a message and a cause. This is trivial to do in Java.

1
2
3
4
5
6
7
8
9
10
11
public class CustomException extends RuntimeException {
  
  public CustomException(String msg){
      super(msg);
  }
  
  public CustomException(String msg, Throwable cause){
      super(msg, cause);
  }
  
}

But next to impossible to do in Scala. Scala requires that every class have a single primary constructor, and that constructor is the only one that’s allowed to call a superclass constructor.

The cleanest thing I could come up with was to use the one argument constructor, which only sets a message, and then set the cause after it was constructed. I wrapped that logic up into some methods on the Exception’s companion object so I could use it as a factory.

1
2
3
4
5
object ScalaCustomException {
  def create(msg: String) : ScalaCustomException = new ScalaCustomException(msg)

  def create(msg: String, cause: Throwable) = new ScalaCustomException(msg).initCause(cause)
}

This works well enough without setting a cause.

1
2
3
4
scala> throw CustomException.create("bad things")
CustomException
  at CustomException$.create(CustomExceptionScala.scala:6)
  ...

And with setting a cause.

1
2
3
4
5
6
7
8
9
scala> val cause = new RuntimeException("cause")
cause: java.lang.RuntimeException = java.lang.RuntimeException: cause

scala> throw ScalaCustomException.create("bad things", cause)
ScalaCustomException
  at ScalaCustomException$.create(CustomExceptionScala.scala:8)
  ...
Caused by: java.lang.RuntimeException: cause
  ...