请参阅下面的两个示例。假设这两个类都包含属于广泛使用的 API 库的公共方法。
AClass.java
更容易编写(由于噪音较小,可能更容易阅读),但是当检测到错误时,堆栈跟踪中的第一个方法是内部方法(nullCheck(...)
),异常消息不引用该方法。这会给用户造成不必要的混乱吗?我的意思是用户思考:"这是一个引发异常的内部方法,一定是库中的错误,而不是我的程序的错误。真是个**没用的图书馆..."
BClass.java
编写起来更烦人(及其所有 if 语句),但是当检测到错误时,堆栈跟踪的第一行会精确定位首次检测到错误的 API 方法(由用户调用)。这是否更有可能让用户认为:"我正在从我的代码中调用该方法,一定是我传入的参数有问题"。
在公共 API 中检查参数的有效性时,哪种方式是引发异常的首选方法?还是这两个例子被认为是相等的?
public class AClass {
public void publicApiMethod1(String a){
nullCheck(a, "a");
// do something...
}
public void publicApiMethod2(String a, String b, String c){
nullCheck(a, "a");
nullCheck(b, "b");
nullCheck(c, "c");
// do something...
}
private void nullCheck(Object a, String argName) {
if(a == null){
throw new NullPointerException("Null argument: " + argName);
}
}
}
public class BClass {
public void publicApiMethod1(String a){
if(a == null){
throw new NullPointerException("Null argument: 'a'");
}
// do something...
}
public void publicApiMethod2(String a, String b, String c){
if(a == null){
throw new NullPointerException("Null argument: 'a'");
}
if(b == null){
throw new NullPointerException("Null argument: 'b'");
}
if(c == null){
throw new NullPointerException("Null argument: 'c'");
}
// do something...
}
}
如果您的错误消息是描述性的(确实如此),则没有人会费心查看堆栈跟踪。因此,第一种形式更好,因为它封装了验证逻辑。
请注意,各种库中有很多可供使用的断言方法,请参阅:Objects.requireNonNull
和 Validate.notNull
)。
我会这样做,基本上是基于你的AClass
:
public void publicApiMethod2(String a, String b, String c){
try {
nullCheck(a, "a");
nullCheck(b, "b");
nullCheck(c, "c");
}
// catch the NullPointerException,
// and any other Exception explicitly thrown by your validation methods
catch (NullPointerException e) {
throw new IllegalArgumentException(e.getMessage());
}
// do something...
}
这样,用户就会获得一条显式消息和一个指向公共方法的堆栈跟踪。
在你的b类中为什么不使用
if( (c == null)|| (b == null)|| (a == null) ){
//thrown my red ball
}
不那么烦人,更具可读性
我认为A类很好,只要你用@throws
清楚地记录它。我已经看到很多库会在堆栈中更深入地投入。重要的是让用户了解为什么抛出错误以及他们可以做些什么来避免它。