Home > Mobile >  How to catch exception which happens at initialization of Java class field?
How to catch exception which happens at initialization of Java class field?

Time:09-22

If there is an exception which happens during the initialization of the class field, how would you catch it?

For instance:

class a{
    int a = 1 / 0;
}

Here exception occurs at the field level.

I know that I could do:

class a {
    a() {
        try {
            this.a = 1 / 0;
        } catch (Throwable a) {}
    }

    int a;
}

But just out of curiosity, is it possible to do it while initializing the field?

Additional info: I am asking this because in my newest project I have one field which I want to initialize to the new instance of the object, and it would be cool to just write a = new Object(); but I can not since the constructor of that particular type throws the checked exception.

CodePudding user response:

is it possible to do it while initializing the field?

You can define a method:

class a {
  int a = aValue();

  private int aValue() {
    try
    {
      return 1/0;
    }
    catch (Throwable a){
      // ...but now you need to return something, or (re)throw an exception.
    }
  }
}

or use an instance initializer:

class a {
  int a;

  {
    try
    {
      this.a=1/0;
    }
    catch (Throwable a){
      // You don't need to do anything here, unless `a` were `final`.
    }
  }
}

but note that instance initializers are inlined into the constructor (or, at least, any constructor that invokes super(...) explicitly or implicitly, rather than this(...)), so this is effectively the same as putting it in the constructor as in the question.

CodePudding user response:

It's really hard to catch those. As a consequence, it is highly advisable to ensure that static initializers do not throw anything that one could feasibly want to catch. (e.g. throwing an OutOfMemoryError is fine, it's not likely someone would want to write code that catches this and does an alternative path or attempts to solve the problem).

This generally starts by replacing your static initializer with a method invocation. Replace:

static int a; static {a = 1/0; }

with:

static int a = calculateA();

private static int calculateA() {
    return 1/0;
}

This is just a step along the path, of course. Move the initializing code (the calculateA method) to a separate class and now you can test it on its own, without even running into problem of catching exceptions thrown during static init of a class.

Once you've taken care of this, you can use this 'trick' to move the problem around. Imagine that the value of a is required for 2 of the methods in this class. Then, 'defer' the exception:

public class Example {
    private static final int a;
    private static final Throwable aProblem;

    static {
        int a = 0;
        Throwable aProblem = null;
        try {
            a = calculateA();
        } catch (RuntimeException e) {
            aProblem = e;
        }
        Example.a = a;
        Example.aProblem = aProblem;
    }

    private static int calculateA() { return 1/0; }

    public static void apiMethodUsingA1() {
        if (aProblem != null) throw aProblem;
        return a;
    }

    public static void apiMethodUsingA2() {
        if (aProblem != null) throw aProblem;
        return a   5;
    }
}

If none of these options are available, for example because A is not written by you and cannot be changed, then you must delegate the A class as 'bad API / crappy library', and you do what you always do when you face such a library: Work around it, accept that you need to write hard to maintain / ugly code, and if it's really bad, write a wrapper to isolate the problems. Maybe even use reflection.

This is one guaranteed way to isolate the exception into a codeblock:

package com.foo;

class Example {
    static int a = 1/0;
}

class Main {
  public static void main(String[] args) throws Exception {
    try {
      Class<?> c = Class.forName("com.foo.Example");
    } catch (ExceptionInInitializerError e) {
      System.err.println("Hey I caught it");
      Throwable actualException = e.getCause();
      // do something with it here
      actualException.printStackTrace(); // not this - this is for debugging only!
    }
  }
}
  • Related