公共类与公共静态内部类的异常
我有两个选项(据我所知,技术上是相同的)来声明仅从特定类 com.XXX.Foo
抛出的自定义异常类:
- 作为包中的公共类:
com.XXX.CustomException
- 作为公共静态内部类:
com.XXX.Foo.CustomException
哪个选项更好?
I have two options (which technically are the same, as I understand) to declare a custom exception class thrown only from a particular class com.XXX.Foo
:
- as a public class in the package:
com.XXX.CustomException
- as a public static inner class:
com.XXX.Foo.CustomException
Which option is better?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我更喜欢同一个包中的(不一定是公共的)类,因为包是描述业务模型的逻辑类组,异常属于技术部分。
用户在查看包时会立即看到有异常,并且不需要读取类 foo 的文件,这对于维护和清晰度/可读性/理解性来说更好。定义自定义异常并告知 API 用户非常好!
只有当内部类明显是相关类的私有对象时,我才会使用它。
然而,我们在这里讨论的是一个主要是传统的问题!
I'd prefer the (not necessarily public) class within the same package, as a package is a logical group of classes depicting a business model, which the exception belongs to as a technical part.
A user will see immediatelly that there's an exception when he looks at the package and does not need to read the file of class foo, which is better for maintenance and clarity/readability/comprehensional reasons. It's very good to define custom exceptions and to tell the API-user about it!
I'd only use an inner class when it's clearly a private thing of the class in question.
Nevertheless, we're talking here about a mainly conventional issue!
一般来说,将异常作为内部类是一个坏主意,因为本质上它们可能会通过各个级别引发,即使在简单的体系结构中也是如此。引发的异常类必须从其包含类中引用,如果类路径未知该类,则可能会发生类似 ClassCastException 的不良情况。
Exceptions as inner classes is a bad idea in general because by nature they may be raised through various levels, even in simple architectures. The exception class being raised must be referenced from its containing class, and if that class is unknown to the classpath something bad would probably happen like a ClassCastException.
如果异常是针对
Foo
类的,我不介意将其保留为public
嵌套类。每当需要将其提取出来时,只需将其提取出来即可。不过,在一般实践中,我从未见过为 Exception 定义任何嵌套类。我也不知道 Java API 中是否存在。
In case the exception is very specific to
Foo
class, I don't mind keeping it as apublic
Nested class. And whenever it seems like a time to extract that out, simply extract that out.In general practice, I have never seen any nested class defined for Exception, though. I also don't know if one exist in Java API.
在我 10 多年的 Java 经验中,我不记得遇到过将公共异常类定义为静态内部类的 API。我无法给你一个具体的理由来解释为什么这样做是一个坏主意,但它肯定会让你的代码不寻常。
鉴于(显然)没有其他人这样做,为什么您认为有必要这样做?我希望你这样做不仅仅是为了“创新”。
(顺便说一句,我知道一些著名的 Java API 将公共静态内部类和接口用于其他用途。我在这里专门讨论异常类的情况。)
In my 10+ years experience with Java, I cannot recall coming across an API where a public exception class was defined as a static inner class. I cannot give you a specific reason why it would be a bad idea to do this, but it would certainly make your code unusual.
Why do you feel that it is necessary to do this, given that (apparently) nobody else does? I hope you are not just doing it to be "innovative".
(BTW, I know that some well-known Java APIs use public static inner classes and interfaces for other things. I'm specifically talking about the case of exception classes here.)
我绝对可以想到我更喜欢将异常作为静态内部类而不是同一包中的类的情况。
不这样做的原因似乎是:
我认为这些论点中的任何一个都没有令人信服的。
对于第一点,为什么这个假设的未来重用机会应该出现在同一个包中?这个论点得出的结论是,我们应该将所有异常类放在包层次结构中尽可能高的位置,这样当我们发现将来有机会重用相同的异常时,我们就不必引入对其最初定义位置的依赖。
但即使没有“走向极端”这一点,也要考虑一个异常,旨在传达类
Foo
被给予了错误的输入。如果我将其命名为Foo.InvalidInput
,那么名称很短,而且与Foo
的关联不可能被忽略。如果我将它放在Foo
类之外并调用它FooInvalidCriteria
,那么无论如何我都无法从类Bar
中重用它,而不更改其名称(相当于改变其位置)。但最糟糕的是,如果我将它留在
Foo
之外并保持其名称像InvalidInput
一样通用。然后,当我后来意识到 Bar 也可能有无效输入并使其开始抛出此异常时。一切都编译并运行良好,只是现在所有捕获InvalidInput
并假设它们正在处理来自Foo
的错误的地方现在也可以处理来自Bar
的错误code> 如果Foo
碰巧在内部使用Bar
,可能会导致抛出此异常。这很容易导致代码破坏。现实情况是,采用先前被认为专门指示一个类中出现的情况的异常并将其重新用作通用错误类是一种接口更改,而不仅仅是内部实现更改。一般来说,要正确执行此操作,您必须重新访问捕获异常的所有站点并确保它们仍然正确,因此让编译器告诉您所有使用站点(因为您必须更改名称和/或导入路径)是一件好事。您可能创建静态内部类的任何异常都不适合在其他上下文中重用,无论您实际上是否将其创建为内部类。
至于第二个点......“没有其他人这样做”永远不会影响任何事情。要么它确实是错误的做法,那么就会有其他理由不这样做,所以“没有人这样做”的争论是没有必要的。或者它不是。而且这个特定的例子也不会非常复杂和难以理解,所以即使是“这是出乎意料的,所以即使理论上是个好主意,人们也会很难理解它”的论点也不是很强。
I can definitely think of situations where I'd prefer the exception to be a static inner class than merely a class in the same package.
The reasons not to do so seem to be:
I do not find either of those arguments at all convincing.
For the first point, why should this hypothetical future opportunity for re-use arise in the same package? This argument leads to the conclusion that we should put all exception classes as high as possible in the package hierarchy so that when we discover a future opportunity to reuse the same exception we don't have to introduce a dependency on where it was originally defined.
But even without the "taken to extremes" point, consider an exception intended to convey that class
Foo
was given wrong input. If I call itFoo.InvalidInput
, the name is short and the association withFoo
is impossible to miss. If I put it outside theFoo
class and call itFooInvalidCriteria
, then I can't reuse it from classBar
anyway, without changing its name (equivalent to changing its location).But worst is if I leave it outside
Foo
and keep its name general likeInvalidInput
. Then when I later realise thatBar
might have invalid input too and make it start throwing this exception. Everything compiles and runs fine, only now all the places that were catchingInvalidInput
and assuming they were handling errors fromFoo
could now also be handling errors fromBar
ifFoo
happens to useBar
internally in a way that could cause this exception to be thrown. This could easily cause code breakage.The reality is that taking an exception that was previously conceived as specifically indicating a situation that arises in one class and re-using it as a general error class is an interface change, not just an internal implementation change. To do so correctly in general you must revisit all the sites where the exception is caught and make sure they're still correct, so having the compiler tell you about all the use sites (because you have to change the name and/or import path) is a good thing. Any exception that you might make a static inner class is inappropriate for reuse in other contexts no matter whether you actually make it an inner class or not.
And as for the second dot point... "nobody else does it" never bears on anything. Either it really is the wrong thing to do, so there will be other reasons not to do it, so the "nobody else does it" argument is unnecessary. Or it isn't. And it's not like this particular example would even be terribly complicated and hard to understand, so not even the "it's unexpected so people will have trouble following it even if it's a good idea in theory" argument is very strong.