13

When I try to throw an exception in an instance initialization (not class initialization) block I get the error:

initializer must be able to complete normally

Why is it not allowed although Java does it itself?

The following example creates four classes. The class A fails during instantiation because of an ArithmeticException. This can be and handled with a catch. The same for B which fails with a NullPointerException. But when I try to throw a NullPointerException on my own as in C the program does not compile. And I get the same error when I try to define my own RuntimeException as in D. So:

How can I do the same as Java does itself?

// -*- compile-command: "javac expr.java && java expr"; -*-

class expr
{
    class A
    {
        int y;
        {{ y = 0 / 0; }}
    }

    class B
    {
        Integer x = null;
        int y;
        {{ y = x.intValue(); }}
    }

    class C
    {
        {{ throw new NullPointerException(); }}
    }

    class Rex extends RuntimeException {}

    class D
    {
        {{ throw new Rex(); }}
    }

    void run ()
    {
        try { A a = new A(); }
        catch (Exception e) { System.out.println (e); }

        try { B b = new B(); }
        catch (Exception e) { System.out.println (e); }

        try { C c = new C(); }
        catch (Exception e) { System.out.println (e); }

        try { D d = new D(); }
        catch (Exception e) { System.out.println (e); }
    }

    public static void main (String argv[])
    {
        expr e = new expr();
        e.run();
    }
}
ceving
  • 21,900
  • 13
  • 104
  • 178

7 Answers7

19

initializer must be able to complete normally

means that there must be a possible code path that doesn't throw an exception. Your examples unconditionally throw, and are therefore rejected. In the other examples, the static analysis doesn't go far enough to determine that they also throw in all cases.

For example,

public class StaticThrow {
    static int foo = 0;
    {{ if (Math.sin(3) < 0.5) { throw new ArithmeticException("Heya"); } else { foo = 3; } }}
    public static void main(String[] args) {
        StaticThrow t = new StaticThrow();
        System.out.println(StaticThrow.foo);
    }
}

compiles, and when run throws

Exception in thread "main" java.lang.ArithmeticException: Heya
        at StaticThrow.<init>(StaticThrow.java:3)
        at StaticThrow.main(StaticThrow.java:5)
Daniel Fischer
  • 181,706
  • 17
  • 308
  • 431
  • 5
    The compiler doesn't analyse the code far enough to see that that will always throw. In your examples, you put `{{ throw something; }}` in the static initialiser, and that's obvious. Try something that will always be true, but is not obvious, `{{ if (Math.sin(3) < 0.5) { throw new ArithmeticException; } else { whatever; } }}`. – Daniel Fischer Nov 23 '12 at 16:41
  • 2
    Thanks! `if (true) throw ...` is even enough. – ceving Nov 23 '12 at 16:58
  • I wasn't sure whether it would, so I took something I was sure the compiler wouldn't see through. – Daniel Fischer Nov 23 '12 at 16:59
  • Exception checking is really one of Java's most half-assed (or should I say stupid) ideas. – ceving Nov 23 '12 at 17:01
  • 1
    I wouldn't call it a stupid idea, it just turned out to be not so good. At the time, saying that methods must document what kind of exceptions they throw so that the callers know what the need to handle [except runtime exceptions that aren't expected to be recoverable] wasn't a stupid idea. It just didn't work as well as hoped, but then it was too late to change (or is it still possible?). – Daniel Fischer Nov 23 '12 at 17:09
  • 1
    @DanielFischer: I don't think it would be impossible (or even breaking) to change; they could say that the `throws` clause is no longer mandatory. Maybe infer leaked exception types, if you want to keep `get*ExceptionTypes` working. Code that included it would still compile, and already-compiled code already handles the exceptions. The Java guys would need a compelling reason to change the language, though -- you might have noticed they're pretty resistant to doing so, even when the change is a net improvement. Plus, i bet a bunch of them still even consider checked exceptions a good idea. – cHao Nov 23 '12 at 18:34
  • @cHao Yes, the technical problems are solvable. The psychological aspect may be the unsurmountable barrier. – Daniel Fischer Nov 23 '12 at 18:50
5

Java is designed to have minimal features and complexity is only added when there is a very good reason to do so. Java doesn't ask; why not, it asks; Do I really need to support this? (and even then doesn't sometimes ;)

The code for an initialiser block must be inserted into each constructor, having a block which the compiler knows doesn't complete normally to a condition the compiler finds too difficult to generate code for.

The compiler could be made to compile this code but its unlikely to be of any use.


It won't help you in this specific case but it useful to know that.....

Checked exceptions must be declared and there is no way to declare checked exception in a static or an instance initialiser block.

Instead you can catch and handle, or wrap the checked exception. (Or using tricks, rethrow it)

Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
  • And how to create an unchecked exception? – ceving Nov 23 '12 at 16:35
  • 2
    Unchecked exceptions extend `RuntimeException`. There's no good way to take an exception that would otherwise be checked, like `IOException` for example, and make it unchecked. But if you create a new type that extends `RuntimeException` either directly or indirectly, then you can throw an instance of it without having to add a `throws` clause. – cHao Nov 23 '12 at 16:54
3
{ throw new Rex(); }

That means that the instance will be never initialized properly. There should be some condition where the instance can be initialized properly. e.g.

{ if(true) { throw new Rex(); } } //It doesn't complain here

If the exception being thrown is a checked-exception then you must add it to the constructor's throws clause. e.g.

public class MyObject {
    { 
        //...
            throw new Exception();
        //...
    }

    public MyObject() throws Exception {

    }
}
Bhesh Gurung
  • 50,430
  • 22
  • 93
  • 142
3

You are actually allowed to throw an exception in an initialization block, but you must mark all constructors with the "throws" keyword, if your exception is a checked one.

You will get an compiling error if your exception will always be thrown, but something like this is perfectly legal:

class Foo {

{{
    if(1 == 1) {
        throw new Exception();
    }
}}

public Foo() throws Exception {

}

}

Hope this clarifies a few things.

comanitza
  • 1,103
  • 2
  • 10
  • 17
2

This is covered by section 8.6 of the Java Language Specification (Java SE 7).

It is a compile-time error if an instance initializer cannot complete normally (§14.21).

14.21 defines what it means to be unreachable. Note in particular

Every other statement S in a non-empty block that is not a switch block is reachable iff the statement preceding S can complete normally.

and

A break, continue, return, or throw statement cannot complete normally.

More complicated analysis would be possible (and could still generate warnings), but these are a set of rules which are understandable, consistently implementable and don't restrict future development of the language particularly.

So why would we want to reject programs with (definitely) unreachable statements? Because they almost certainly represent bugs (in finished code). (if statements behave peculiarly to support dodgy conditional compilation.)

There aren't any unreachable statements, so why must instance initialers be able to complete normally (not a requirement for constructors in order to support non-instantiable classes)? Because that requires non-local analysis which Java does not do in order to remain reasonably simple and a statement may be dropped in or just order of code rearranged during maintenance.

It is probably worth noting that a body of opinion believes that Java is overcomplicated by this relatively simple analysis, together with definitie assignment rules.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
1

From http://www.artima.com/designtechniques/initializationP.html

The code inside an instance initializer may not return. Except in the case of anonymous inner classes, an instance initializer may throw checked exceptions only if the checked exceptions are explicitly declared in the throws clause of every constructor in the class. Instance initializers in anonymous inner classes, on the other hand, can throw any exception.

AlexWien
  • 28,470
  • 6
  • 53
  • 83
  • _Instance initializers in anonymous inner classes, on the other hand, can throw any exception._ This is a powerful little feature, thanks for finding this detail – blueimpb Jun 16 '16 at 17:55
0

It'll cause the rest of statements apparently unreachable, which Java tries to forbid.

irreputable
  • 44,725
  • 9
  • 65
  • 93