【问题标题】:How to break out initialization block?如何打破初始化块?
【发布时间】:2019-06-22 07:01:30
【问题描述】:

我有一门课是这样的

class Some {

    private enum Inner {
    }
}

我正试图在我的测试类的初始化块中找到 Inner 类。

class SomeTest {

    private static final Class<?> INNER_CLASS;

    {
        for (final Class<?> declaredClass: Some.class.getDeclaredClasses()) {
            if (declaredClass.getSimpleName().equals("Inner")) {
                INNER_CLASS = declaredClass;
                // Variable `INNER_CLASS` might be assigned in loop
                // break? return?
            }
        }
        throw new ExceptionInitializerError("failed to find Inner.class");
    }
}

编译器不喜欢这样,我找不到更好的方法。

我该如何解决这个问题?这有什么好的模式吗?

【问题讨论】:

  • 编译器对此有何不满意?我没有看到 return 声明,这意味着您的 ExceptionInitializerError 将始终被抛出。
  • 不应该是静态初始化块吗?

标签: java initialization-block


【解决方案1】:

staticinstance 初始化块不能抛出已检查异常,因为无法声明这些块会抛出这些异常。将ExceptionInitializerError 更改为RuntimeException(或任何子类)并将您的代码包装在try-catch

除了这里,你没有返回也没有打破,因此你总是抛出异常。

至于“爆发”很好,你不知道。您必须编写该块,因为它是 void 方法的主体,但限制是您不能在任何地方使用 return

【讨论】:

    【解决方案2】:

    您的代码存在一些问题:

    1. 您的异常名称不正确。您尝试抛出的异常称为ExceptionInInitializerError 而不是ExceptionInitializerError。这就是它无法编译的一个原因

    2. 从不1 抛出ErrorError 的子类。

    3. 如果需要抛出未经检查的异常,请抛出RuntimeException。或者更好的是,选择更具体的内容或定义并使用您自己的自定义(未检查)异常类。

    4. 这应该(可能)是一个static 初始化程序块,而不是一个普通的(实例)初始化程序。您希望此代码执行一次 ...而不是每次创建 SomeTest 实例时。

    5. 退出 static 初始化程序块是您想要避免的事情。它基本上给你留下了一个死的应用程序......因为封闭类和任何依赖它的类都变得不可初始化。


    话虽如此,以下可能是更合适的结构:

     static {
         BlahType tmp = null;
         label: {
             for (...) {
                 if (...) {
                     tmp = ...;
                     break label;
                 }
             }
             throw new SomeException(...);
         }
         FINAL_VAR = tmp;
    }
    

    请注意,我们需要对FINAL_VAR 进行最终分配,以确保它被明确分配。 (我的猜测是你得到编译错误的第二个原因。)

    上面写的更自然的方式是:

    static {
         BlahType tmp = null;
         for (...) {
             if (...) {
                 tmp = ...;
                 break;
             }
         }
         if (tmp == null) {
             throw new SomeException(...);
         }
         FINAL_VAR = tmp;
    }
    

    1 - 可能有点太强了。我会说投掷AssertionError 是可以的...假设您打算因为它永远不会被捕获/恢复。在这种情况下,无论如何恢复都是没有意义的。

    【讨论】:

      【解决方案3】:

      在最终赋值之前使用中间变量。

      class SomeTest {
      
          private static final Class<?> INNER_CLASS;
          static {
              Class<?> innerClass = null;
              for (final Class<?> declaredClass: Some.class.getDeclaredClasses()) {
                  if (declaredClass.getSimpleName().equals("Inner")) {
                      innerClass = declaredClass;
                  }
              }
              if (innerClass == null) {
                  throw new ExceptionInitializerError("failed to find Inner.class");
              }
              INNER_CLASS = innerClass;
          }
      }
      

      【讨论】:

        【解决方案4】:

        有几个问题:

        1. 总是抛出异常
        2. 您正在循环中分配给最终变量
        3. 初始化块不是静态的并且分配给静态最终变量

        看看这个:

        class SomeTest {
        
            private static final Class<?> INNER_CLASS;
        
            static {
                Class<?> foundClass = null;
                for (final Class<?> declaredClass : Some.class.getDeclaredClasses()) {
                    if (declaredClass.getSimpleName().equals("Inner")) {
                        foundClass = declaredClass;
                        // Variable `INNER_CLASS` might be assigned in loop
                        // break? return?
                    }
                }
                INNER_CLASS = foundClass;
                // throw new Exception("failed to find Inner.class");
            }
        }
        

        【讨论】:

        • 初始化块不是静态的,而是分配给一个语义上有效的静态变量
        • 分配给 final 在非静态块中无效。不过需要检查。
        • 最终变量是不同的故事。只需澄清第 3 点,即您所说的是静态最终结果。
        猜你喜欢
        • 1970-01-01
        • 2019-11-09
        • 2011-11-09
        • 2016-07-28
        • 2012-02-05
        • 2011-01-26
        • 1970-01-01
        • 1970-01-01
        • 2014-05-19
        相关资源
        最近更新 更多