Return 默认还是引发异常?

Return default or raise an exception?

我对什么被认为是好的做法感到困惑 - 这个决策语言是否依赖?假设我有以下 Java 代码:

public class Stack {
    public Integer pop() {
      if (isEmpty()) return null; // or some exception maybe?
          // else get and return the top item in the stack.
      };
    }
}

pop 方法的客户端需要一些 Integer 值,那么让客户端知道堆栈为空的最佳方法是什么?

你可以抛出 IllegalStateException.

Signals that a method has been invoked at an illegal or inappropriate time.

在空堆栈上调用 pop() 是在不适当的时间调用。

更现代的方法将是return一个Optional<Integer>,这是Java 8中推荐的避免[=14]的方法=] returns.

public class Stack {
  public Optional<Integer> pop() {
    if (isEmpty()) return Option.ofNullable(null);
    return Optional.of(valueOnTheStack);
  }
}

根据 Java API,尝试从空堆栈中弹出一个元素应该产生异常。

因此,我认为对于您的情况,您也应该这样做(假设您在 Java 中实现了这一点)。不过,其他语言可能会以不同的方式处理这个问题。

返回 null 或默认值 通常 是一种不好的做法,应该首选例外。原因是 当出现问题时,您应该始终努力尽快失败 。如果您改为 return null,您将在代码的其他地方发生错误,并且 API 的用户将难以找到问题所在。这叫做Fail Fast.

这个经验法则的一个例外是当你的 API 会让用户依赖异常进行流量控制时,所以如果你的堆栈不支持 isEmpty(),异常不是一个好主意这里。如果您的堆栈只允许 peek()pop()add() - 由于某种原因,isEmpty() 不允许成为 API 的一部分.

对于这两种方法,您的用户的代码会发生什么变化?

选项 1 - 使用 null:

Integer x = stack.pop();
if (x != null) { 
    //do something with x
}

选项 2 - 使用例外:

Integer x = null;
try { 
    x = stack.pop();
} catch (MyException e) { }
//do something with x

第二种实际上是使用异常机制进行流量控制 - 这是 API 设计中的一大缺陷。

如果构建一个API,它应该是抽象的,并且使用异常,因为它会集成在一个Java环境中,然后弹出操作如下。

public Integer pop () throws Exception
{
    if (stack.isEmpty())
        throw new java.util.EmptyStackException();

    return stack.pop();
}

如果我将其构建为 Web 服务,那么我将按以下方式进行。基本上是为了确保通过使用堆栈的return对象来表示不同类型的内部异常的标准化。

堆栈服务

@WebService
class MyStack
{
    StackProvider provider;
    Stack stack;

    public MyStack (StackProvider provider)
    {
        this.provider = provider;
        this.stack = provider.readStack();
    }

    public void sync ()
    {
        provider.syncStack(stack);
    }

    public Integer pop ()
    {
        if (stack.isEmpty())
            return new StackElement(StackElement.STACK_EMPTY);

        return new StackElement(stack.pop());
    }

    public void push (Integer val)
    {
        stack.push(val);
    }
}

堆栈元素

class StackElement
{
    public static final char STACK_EMPTY = 0;
    public static final char ELEMENT_VALID = 1;

    public Integer value;
    public char flag;

    public StackElement (Integer val)
    {
        this.value = val;
        this.flag = StackElement.ELEMENT_VALID;
    }

    public StackElement (char flag)
    {
        this.value = null;
        this.flag = flag;
    }

    public boolean isValid ()
    {
        return (flag == 1);
    }
}

测试

class Test
{
    public static void main (String [] args)
    {
        StackProvider provider = new StackProvider("...");

        // init

        MyStackService service = new MyStackService(provide);
        MyStackServiceSoap soap = service.getMyStackServiceSoap();

        // call pop operation

        StackElement element = soap.pop();

        // checking the value

        if (element.isValid())
          System.out.println("Stack.Pop ~ " + element.value);
        else
        {
          if (element.flag == StackElement.STACK_EMPTY)
            System.out.println("Stack is Empty");
          else
            System.out.println("Unknown error");
        }

    }
}