Why can I define common exception types in Scala?

In Java, it is illegal to define a common 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 stranger, I can use this Scala class in Java code if 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()); } } } 

Compiles, runs and prints a "test".

Is it well-defined according to the JLS and JVM specification or is it just randomly triggering?

Is the Java restriction on general exceptions a purely language restriction or is it also applied to the bytecode (in this case, the bytecode generated by the Scala compiler would not be acceptable)?

Change This is the Scala class that 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 } 
+6
source share
2 answers

Short answer:

  • The JVM specification prohibits throwing and catching parameterized exceptions, but does not care about the declaration. It does not even prohibit that it is simply impossible to represent a type parameter in bytecode, so the question is debatable.
  • JLS forbids them to declare, because in any case you cannot use them.

Long answer:

The Java language specification says (ยง8.1.2) about the declaration of such a class:

This is a compile-time error if the general class is a direct or indirect Throwable subclass (ยง11.1.1).

This restriction is necessary because the catch mechanism of the Java virtual machine only works with non-generic classes.

And he talks about throwing an exception (ยง14.18):

The expression in the throw expression should mean either

1) a variable or value of a reference type that is assigned (ยง5.2) to the Throwable type, or

2) null reference or compile-time error.

An Expression reference type will always be a class type (since no interface types are assigned to Throwable) that is not parameterized (since the Throwable subclass cannot be general (ยง8.1.2)).

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

There is still information about a parameter of this type where the class is defined, but not where it is used! Regardless of the declaration of a parameterized exception, this is not possible at the JVM level :

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

The implementation of exception exclusion is that there is an exception table that specifies the bytecode ranges to view and the class associated with it. This class cannot have a type parameter, since there is no way to describe them in bytecode (JVM specification, ยงยง2.10 and ยง3.12).

Due to type erasure, the throw clause applies only to Foo , and these two catch methods become two entries in the exception table that belong to the Foo class, which is useless and impossible in any case. As a result, this syntax is not possible in the Java language, and you can only catch Foo .

And as a consequence of this, it becomes quite useless and potentially dangerous to be able to declare parameterized exceptions. Therefore, they were generally banned in this language, although the JVM itself does not care.

+6
source

Here is the answer that we have voiced so far in the discussion.

Is it well defined according to the JLS and JVM specification, or is it just randomly triggered?

It should not be clearly defined in accordance with the Java Language Specification, as it is a different language.

The JVM, OTOH, has no problem throwing and catching such exceptions, because due to type erasure this has nothing to do with bytecode.

However, the interesting question remains, why does Java prohibit the Throwable extension with common classes in the first place.

+2
source

Source: https://habr.com/ru/post/950513/


All Articles