公共类异常 vs 公共静态内部类异常

39

我有两个选项(技术上来说是一样的,据我所知)来声明一个自定义异常类,仅从特定类com.XXX.Foo抛出:

  • 作为包中的公共类:com.XXX.CustomException
  • 作为静态内部类:com.XXX.Foo.CustomException

哪个选项更好?


1
同样的问题和选项可以应用于任何类。异常是否具有特别适合嵌套的特征,或者这个问题只是在询问何时使用嵌套类?(https://docs.oracle.com/javase/tutorial/java/javaOO/whentouse.html) - jaco0646
5个回答

19

如果异常与Foo类密切相关,我不介意将其保留为public嵌套类。如果需要提取它,只需提取即可。

但是,在一般实践中,我从未见过为异常定义嵌套类的情况。我也不知道Java API中是否存在这样的类。


12

在我的10年以上的Java经验中,我无法回忆起遇到过将公共异常类定义为静态内部类的API。我不能给你一个具体的原因,说明为什么这样做是不好的,但这肯定会使你的代码变得不寻常。

既然明显没有人这样做,为什么你觉得有必要这样做呢?我希望你不是只为了“创新”而这样做。

(顺便说一句,我知道一些知名的Java API使用public static内部类和接口来实现其他功能。我特别讲的是异常类的情况。)


2
看一下 Apache Lucene 3.5 中的这个类:org.apache.lucene.search.BooleanQuery.TooManyClauses - yegor256
1
好的。10多年来只有一个例子。(但他们为什么这样做?) - Stephen C
2
我认为,仅当异常从一个类中抛出时才有意义。在这种情况下,它应该被定义为静态内部类。当您删除该类时,异常也会被消除。换句话说,它们将是“紧密耦合的”。 - yegor256
3
是的,这就是这些异常存在的目的(你刚定义了它)——通过设计使它们不可重复使用。当我们想要将异常与类密切相关(没有任何重用的能力)时,将其设为静态内部异常。明白了吗? - yegor256
5
@Stephen C: 不,这真的是一个好主意。让我们稍微绕个路:在 java.util.HashMap 中,你有一个名为 Entry 的内部类。你认为它为什么是一个内部类?因为它与 HashMap 密切相关,在 HashMap 上下文之外没有用处,如果在其他地方使用可能会产生误导。将它嵌入到 HashMap 中还可以创建一个完整的封装数据结构作为一个“单元”。同样的方法也适用于异常处理。 - Alexis Dufrenoy
显示剩余3条评论

9
我肯定可以想到一些情况,我会更喜欢将异常作为静态内部类而不仅仅是在同一个包中的类。
不这样做的原因似乎是:
- 将异常与抛出它的类耦合在一起,使它无法在其他上下文中重用。 - 没有其他人这样做。
我并不认为这两个论点有说服力。
对于第一个观点,为什么这种假设性的将来重用机会会在同一个包中出现?这个论点导致的结论是,我们应该尽可能将所有异常类放在包层次结构的最高位置,以便在发现未来重用相同异常的机会时,我们不必依赖于其最初定义的位置。
但即使没有“被极端利用”的观点,考虑一个旨在传达类Foo输入错误的异常。如果我将其命名为Foo.InvalidInput,那么名称很短,与Foo的关联是不可避免的。如果我将其放在Foo类外,并将其命名为FooInvalidCriteria,则我不能从类Bar中重用它,除非更改其名称(相当于更改其位置)。
但最糟糕的是,如果我将其留在Foo之外,并保持其名称通用,例如InvalidInput。然后,当我稍后意识到Bar也可能存在无效输入并开始抛出此异常时。所有编译和运行都很好,只有现在处理InvalidInput并假设它们正在处理来自Foo的错误的所有位置仍然可以处理来自Bar的错误,如果Foo以一种可能导致抛出此异常的方式在内部使用Bar。这可能会轻松引起代码破坏。
现实情况是,将先前被构想为专门指示一个类中出现的情况的异常作为一般错误类重新使用是一个接口更改,而不仅仅是内部实现更改。要在一般情况下正确执行此操作,您必须重新访问捕获异常的所有站点,并确保它们仍然正确,因此让编译器告诉您所有使用站点(因为您必须更改名称和/或导入路径)是一件好事。无论是否实际上将其设置为内部静态类,任何您可能要创建的异常都不适合在其他上下文中重用。

至于第二个点……“其他人都不这样做”从来没有什么意义。要么这确实是错误的事情,因此会有其他理由不这样做,所以“其他人都不这样做”的论据是不必要的。要么就不是。而且,即使这个例子在理论上是一个好主意,它也并不是非常复杂和难以理解,所以甚至连“这是出乎意料的,所以人们即使是一个好主意也会有跟随的困难”的论据也不是非常强有力。


3

我更喜欢在同一包中使用(不一定是公共的)类,因为包是描述业务模型的逻辑类组,异常作为技术部分属于其中。

当用户查看包时,将立即看到异常,而不需要阅读类foo的文件,这有助于维护和提高代码清晰度、可读性和理解性。定义自定义异常并告知API用户非常好!

只有在明确是该类的私有内容时才会使用内部类。

尽管如此,我们在这里讨论的主要是一个约定俗成的问题!


如何创建一个继承javax.swing.RowFilter.Entry的类? - Adeel Ansari
2
包用于分组逻辑模型,因此在我的团队中,我们会遵守每个类都有自己的文件的约定。包中的业务模型很可能相当紧密地耦合在一起,因此我看不出使用嵌套类的好处。嵌套类基本上表达了:我与这个其他类密不可分。对于某些API,设计成这样可能是有好处的,例如如果包内整个模型耦合度比较松散。 - Falcon
2
你可以扩展静态内部类。 - Jim Tough
1
@Falcon: “静态内部类也不是很面向对象,由于无法从静态类继承,因此从架构设计的角度来看,我会至少省略掉静态。” 这完全是错误的。 - Adeel Ansari
@Falcon:我知道你的想法。但是你必须明白,static在类上的作用方式完全不同。请参考这个线程以获得澄清,https://dev59.com/GXVD5IYBdhLWcg3wJIIK。 - Adeel Ansari
显示剩余3条评论

-3

将异常作为内部类通常不是一个好主意,因为它们的本质是可能在各个级别上引发的,即使在简单的架构中也是如此。所抛出的异常类必须从其包含类中引用,如果该类未知于类路径,则可能会发生诸如ClassCastException等不良情况。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接