【问题标题】:Is there a way to throw an exception without adding the throws declaration?有没有办法在不添加 throws 声明的情况下抛出异常?
【发布时间】:2010-12-23 14:24:04
【问题描述】:

我有以下情况。

我有一个继承自另一个基类并覆盖方法的 Java 类。 基本方法不会抛出异常,因此没有throws ... 声明。

现在我自己的方法应该能够抛出异常,但我可以选择任一方法

  • 吞下异常或
  • 添加 throws 声明

两者都不令人满意,因为第一个会默默地忽略异常(好吧,我可以执行一些日志记录),第二个会因为不同的方法头而产生编译器错误。

public class ChildClass extends BaseClass {

        @Override 
        public void SomeMethod() {
            throw new Exception("Something went wrong");
        }
}

【问题讨论】:

    标签: java exception


    【解决方案1】:

    如果你真的想的话,你可以抛出未经检查的异常而不必声明它们。未经检查的异常扩展RuntimeException。扩展 Error 的 Throwable 也未选中,但只能用于完全无法处理的问题(例如无效的字节码或内存不足)。

    作为一个具体案例,Java 8 添加了UncheckedIOException 用于包装和重新抛出IOException

    【讨论】:

    • 效果很好,我必须重新抛出 RuntimeException,因为异常来自另一个方法,但效果很好,谢谢。
    【解决方案2】:

    这里有一个技巧:

    class Utils
    {
        @SuppressWarnings("unchecked")
        private static <T extends Throwable> void throwException(Throwable exception, Object dummy) throws T
        {
            throw (T) exception;
        }
    
        public static void throwException(Throwable exception)
        {
            Utils.<RuntimeException>throwException(exception, null);
        }
    }
    
    public class Test
    {
        public static void main(String[] args)
        {
            Utils.throwException(new Exception("This is an exception!"));
        }
    }
    

    【讨论】:

    • 我想知道这是如何工作的?我会做一些研究,但是你有什么资源可以帮助我吗? :-)
    • T 推断为 RuntimeException。在这里回答stackoverflow.com/questions/41380656/… 和这里stackoverflow.com/questions/31316581/…
    • 太棒了!这个技巧也可以应用于 lambda 表达式/块,允许在没有任何 throws 声明的情况下将检查异常方法分配给 SAM 接口。
    • 有额外的超级额外好处,不必处理不安全的问题。
    • 为什么需要虚拟参数?另外,如果没有通用方法,这可以工作吗?
    【解决方案3】:

    第三种选择是退出异常检查(就像标准 API 本身有时必须做的那样)并将检查的异常包装在 RuntimeException 中:

    throw new RuntimeException(originalException);
    

    您可能希望使用RuntimeException 的更具体的子类。

    【讨论】:

      【解决方案4】:

      我只是想添加一个替代答案,纯粹作为仅供参考

      是的,有一种方法可以在不添加 throws 声明的情况下引发检查异常,方法是使用 sun.misc.Unsafe 类。以下博客文章对此进行了描述:

      Throw a checked exception from a method without declaring it

      示例代码:

      public void someMethod() {
        //throw a checked exception without adding a "throws"
        getUnsafe().throwException(new IOException());
      }
      
      private Unsafe getUnsafe() {
        try {
          Field field = Unsafe.class.getDeclaredField("theUnsafe");
          field.setAccessible(true);
          return (Unsafe) field.get(null);
        } catch (Exception e) {
          throw new RuntimeException(e);
        }
      }
      

      但是,不建议这样做。最好像其他一些答案中所述包含未经检查的异常。

      【讨论】:

      • 他们称该类为Unsafe是有原因的。
      【解决方案5】:

      为什么不抛出未经检查的异常?这不必声明。

      有两种选择

      • 将已检查异常与未检查异常包装起来。
      • 不要让编译器知道你正在抛出一个检查异常,例如Thread.currentThread().stop(e);
      • 在 Java 6 中,如果异常是 final,您可以重新抛出异常,并且编译器知道您可能捕获了哪些已检查异常。
      • 在 Java 7 中,如果异常是有效的最终异常,您可以重新引发异常,即您不会在代码中对其进行更改。

      当您在代码中抛出检查异常并在调用代码中捕获它时,后者更有用,但中间的层对异常一无所知。

      【讨论】:

      • 第二种方法也很有趣。但目前,包装异常正是我所需要的。
      • @OrangeDog,既然你已经阅读了这篇文章,你能告诉我在当前线程上使用 stop() 和抛出一个包装的异常有什么区别吗? ;)
      • “以下方法在行为上与 Java 的 throw 操作相同,但绕过了编译器尝试保证调用方法已声明它可能抛出的所有检查异常”如何更好地包装异常?
      • "停止线程会导致它解锁所有已锁定的监视器。如果以前受这些监视器保护的任何对象处于不一致状态,其他线程现在可能会以不一致的方式查看这些对象状态。[...] 与其他未经检查的异常不同 [...] 用户不会收到有关其程序可能已损坏的警告。”
      【解决方案6】:

      是的,这是有原因的,但完全不建议您使用:

      Java 不安全包

      getUnsafe().throwException(new IOException());
      

      此方法会抛出已检查异常,但您的代码不会强制捕获或重新抛出它。就像运行时异常一样。

      【讨论】:

        【解决方案7】:

        下面是一个拦截已检查异常并将它们包装在未检查异常中的示例:

        public void someMethod() {
           try {
              doEvil();
           }
           catch (IOException e)
           {
               throw new RuntimeException(e);
           }
        }
        

        【讨论】:

          【解决方案8】:

          如果你使用Project lombok 并且想要在没有throws 声明的情况下抛出检查异常,你可以在方法中添加@SneakyThrows

          public void yourCaller(){
              yourMethod();
          }
          @SneakyThrows
          public void yourMethod(){
              throw new Exception("Something went wrong");
          }
          
          

          这可以抛出已检查的异常,而调用者不需要捕获它们。

          Lombok 提供了一个annotation processor,它在编译时修改代码。使用@SneakyThrows,它在没有throws 声明的情况下捕获并重新抛出异常。

          the description of @SneakyThrows 中所述,它将代码转换为类似的内容:

          public void yourMethod() {
              try {
                  throw new Exception("Something went wrong");
              } catch (Exception t) {
                  throw Lombok.sneakyThrow(t);
              }
          }
          

          来自the sources of Lombok.sneakyThrow()

          public static RuntimeException sneakyThrow(Throwable t) {
              if (t == null) throw new NullPointerException("t");
              return Lombok.<RuntimeException>sneakyThrow0(t);
          }
          
          @SuppressWarnings("unchecked")
          private static <T extends Throwable> T sneakyThrow0(Throwable t) throws T {
              throw (T)t;
          }
          

          如您所见,它使用泛型诱使 Java 认为这是一个未经检查的异常,如 this answer 所示。

          【讨论】:

            【解决方案9】:

            在 Java 8 中,由于类型推断,可以更容易地在不声明的情况下抛出已检查异常。

            public class Main {
                public static void main(String[] args) {
                  throwException(new Exception("exception"));
                }
                
                public static <T extends Throwable> void throwException(Throwable t) throws T {
                    throw (T) t;
                }
            }
            

            【讨论】:

            • 谢谢,但已经在这里发帖 stackoverflow.com/a/18408831/98491
            • @JürgenSteinblock 那不是同一种方法;它需要一个辅助方法,而我的答案不需要。
            【解决方案10】:

            是的,使用类型转换到运行时异常并引发运行时异常。

            像这样创建一个异常帮助器类。

            public class ExceptionHelper {
                public static  <T> void throwException(Throwable t) throws Throwable {
                    throw (Throwable) t;
                }
            }
            
            class ServiceClass {
                public void actualFlow() {
                    try {
                        //somethinf
                    } catch (Exception e) {
                        ExceptionHelper.throwException(e);
                    }
                }
            }
            

            【讨论】:

              【解决方案11】:

              你可以在你的方法中使用 try-catch 块来捕获异常。 那么你就不需要声明 throws- 语句了。

              【讨论】:

              • 当然,但是我会吞下异常,这与我想要实现的目标完全相反;)
              【解决方案12】:

              您可以使用从 RuntimeException 或 RuntimeException 本身派生的任何异常

              对抛出异常的代码使用 try-block 并在那里处理它

              【讨论】:

                猜你喜欢
                • 2015-07-11
                • 2010-10-31
                • 2013-11-23
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-10-05
                • 1970-01-01
                相关资源
                最近更新 更多