Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How come I can define generic exception types in Scala?

Tags:

java

jvm

scala

In Java, it's illegal to define a generic exception class. The compiler will refuse to compile the following:

public class Foo<T> extends Throwable {
    // whatever...
}

However, this Scala code compiles just fine:

class Foo[T](val foo: T) extends Throwable

Even weirder, I can use this Scala class in Java code as long as I catch the raw Foo type:

public class Main {
    public static void main(String[] args) {
        try {
            throw new Foo<String>("test");
        }
        catch(Foo e) {
            System.out.println(e.foo());
        }
    }
}

This compiles, runs, and prints "test".

Is this well defined according to the JLS and JVM spec or does it just happen to work accidentally?

Is the Java restriction on generic exceptions purely a language restriction or does it also apply to the bytecode (in which case the bytecode generated by the Scala compiler would be invalid)?

Edit: This is what the Scala class looks after decompilation:

public class Foo<T> extends java.lang.Throwable {
  public T value();
    Code:
       0: aload_0
       1: getfield      #15                 // Field value:Ljava/lang/Object;
       4: areturn

  public Foo(T);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #15                 // Field value:Ljava/lang/Object;
       5: aload_0
       6: invokespecial #22                 // Method java/lang/Throwable."<init>":()V
       9: return
}
like image 931
Tobias Brandt Avatar asked Jul 29 '13 10:07

Tobias Brandt


1 Answers

Short answer:

  • the JVM spec forbids throwing and catching parametrized exceptions, but does not care about declaration. It does not even forbid that, there just is no way to represent the type parameter in bytecode, so the question is moot.
  • the JLS forbids declaring them because there is no way you could use them anyway.

Long answer:

The Java Language Specification says (§8.1.2) about declaring such a class:

It is a compile-time error if a generic class is a direct or indirect subclass of Throwable (§11.1.1).

This restriction is needed since the catch mechanism of the Java Virtual Machine works only with non-generic classes.

And it says about throwing an exception (§14.18):

The Expression in a throw statement must denote either

1) a variable or value of a reference type which is assignable (§5.2) to the type Throwable, or

2) the null reference, or a compile-time error occurs.

The reference type of the Expression will always be a class type (since no interface types are assignable to Throwable) which is not parameterized (since a subclass of Throwable cannot be generic (§8.1.2)).

This restriction was added when generics were added to the Java language because they were not added to the JVM itself: only raw types exist on the JVM.

There is still information about that type parameter where the class is defined, but not where is is used! Independently of the declaration of a parametrized exception, this is impossible on the JVM level:

try {
    throw new Foo<String>("test");
} catch(Foo<Int> e) {
  // int
} catch(Foo<String> e) {
  // string
}

The way exception catching is implemented is that there is an exception table that specify ranges of bytecode to watch and an associated class to catch. That class cannot have a type parameter since there is no way to describe them in bytecode (JVM specification, §2.10 and §3.12).

Because of type erasure, the throw clause only refers to Foo, and those two catch methods would become two entries in the exception table that both refer to the class Foo, which is both useless and impossible anyway. As a consequence that syntax is not possible in the Java language and one can only catch Foo.

And as a consequence to that, it becomes pretty useless and potentially dangerous to be able to declare parametrized exceptions. So they were forbidden altogether in the language, even though the JVM itself does not care.

like image 108
gourlaysama Avatar answered Oct 13 '22 23:10

gourlaysama