首页 文章

有一个return语句只是为了满足语法不良的做法吗?

提问于
浏览
79

请考虑以下代码:

public Object getClone(Cloneable a) throws TotallyFooException {

    if (a == null) {
        throw new TotallyFooException();
    }
    else {
        try {
            return a.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
    //cant be reached, in for syntax
    return null;
}

return null; 是必要的,因为可能会捕获异常,但是在这种情况下,因为我们已经检查它是否为null(并假设我们知道我们调用的类支持克隆)所以我们知道try语句永远不会失败 .

在最后放入额外的return语句只是为了满足语法并避免编译错误(使用注释解释它不会达到)是不好的做法,或者是否有更好的方法来编写这样的东西以便额外的返回声明是不必要的?

13 回答

  • 14

    没有额外退货声明的更清晰的方法如下 . 我也不会 grab CloneNotSupportedException ,但让它去打电话 .

    if (a != null) {
        try {
            return a.clone();
        } catch (CloneNotSupportedException e) {
            e.printStackTrace();
        }
    }
    throw new TotallyFooException();
    

    几乎总是可以摆弄顺序,最终得到比你最初的更直接的语法 .

  • 26

    绝对可以达成 . 请注意,您只在 catch 子句中打印堆栈跟踪 .

    a != null 并且会出现异常的情况下,会到达 return null will . 您可以删除该语句并将其替换为 throw new TotallyFooException(); .

    通常*,如果 null 是方法的结果(即用户期望它并且意味着什么),那么将其作为"data not found"的信号返回或发生异常是 not 一个好主意 . 否则,我不会 null 返回 null .

    Scanner#ioException方法为例:

    返回此Scanner的基础Readable最后抛出的IOException . 如果不存在此类异常,则此方法返回null .

    在这种情况下,返回值 null 具有明确的含义,当我使用该方法时,我可以确定我得到 null 只是因为没有这样的异常而不是因为该方法试图做某事而且失败了 .

    *请注意,即使含义不明确,有时您也希望返回 null . 例如HashMap#get

    返回值null不一定表示映射不包含键的映射; Map 也可能将键明确映射为null . containsKey操作可用于区分这两种情况 .

    在这种情况下, null 可以指示找到并返回值 null ,或者hashmap不包含请求的密钥 .

  • 2

    在最后添加额外的return语句只是为了满足语法并避免编译错误(不会达到解释它的注释)是不好的做法

    我认为 return null 对于无法到达的分支的终点是不好的做法 . 最好抛出一个RuntimeException(也可以接受AssertionError),以便到达该行已经出错的情况并且应用程序处于未知状态 . 大多数像这样(如上所述),因为开发人员遗漏了一些东西(对象可以是非空的和不可复制的) .

    我可能不会使用InternalError,除非我非常确定代码无法访问(例如在 System.exit() 之后),因为我更有可能犯了错误而不是VM .

    我只使用自定义异常(例如 TotallyFooException ),如果达到"unreachable line"意味着与抛出该异常的任何其他地方相同 .

  • 98

    你 grab 了 CloneNotSupportedException ,这意味着你的代码可以处理它 . 但是当你 grab 它之后,你完全不知道当你到达函数的末尾时该做什么,这意味着你不能在这种情况下它是一个代码气味,在我看来你不应该有抓了 CloneNotSupportedException .

  • 5

    我更愿意使用 Objects.requireNonNull() 来检查参数a是否为空 . 因此,当您阅读代码时,参数不应为null,这一点很清楚 .

    为了避免检查异常,我会将 CloneNotSupportedException 作为 RuntimeException 抛出 .

    对于这两者,你可以添加漂亮的文本,意图为什么不应该发生这种情况或者是这种情况 .

    public Object getClone(Object a) {
    
        Objects.requireNonNull(a);
    
        try {
            return a.clone();
        } catch (CloneNotSupportedException e) {
            throw new IllegalArgumentException(e);
        }
    
    }
    
  • 2

    在这种情况下我会写

    public Object getClone(SomeInterface a) throws TotallyFooException {
        // Precondition: "a" should be null or should have a someMethod method that
        // does not throw a SomeException.
        if (a == null) {
            throw new TotallyFooException() ; }
        else {
            try {
                return a.someMethod(); }
            catch (SomeException e) {
                throw new IllegalArgumentException(e) ; } }
    }
    

    有趣的是你说的是"try statement will never fail",但是你仍然不厌其烦地写下你声称永远不会被执行的声明 e.printStackTrace(); . 为什么?

    也许你的信念不是那么牢固 . 这很好(在我看来),因为您的信念不是基于您编写的代码,而是基于您的客户不会违反前提条件的期望 . 更好地在防守方面编制公共方法 .

    顺便说一下,即使 a 的类型是 a.clone() ,您的代码也会被't compile for me. You can'调用 a.clone() Cloneable . 至少Eclipse的编译器这样说 . 表达式 a.clone() 给出错误

    对于Cloneable类型,方法clone()未定义

    我会为你的具体案例做些什么

    public Object getClone(PubliclyCloneable a) throws TotallyFooException {
        if (a == null) {
            throw new TotallyFooException(); }
        else {
            return a.clone(); }
    }
    

    其中 PubliclyCloneable 由...定义

    interface PubliclyCloneable {
        public Object clone() ;
    }
    

    或者,如果您绝对需要参数类型为 Cloneable ,则以下至少编译 .

    public static Object getClone(Cloneable a) throws TotallyFooException {
    //  Precondition: "a" should be null or point to an object that can be cloned without
    // throwing any checked exception.
        if (a == null) {
            throw new TotallyFooException(); }
        else {
            try {
                return a.getClass().getMethod("clone").invoke(a) ; }
            catch( IllegalAccessException e ) {
                throw new AssertionError(null, e) ; }
            catch( InvocationTargetException e ) {
                Throwable t = e.getTargetException() ;
                if( t instanceof Error ) {
                    // Unchecked exceptions are bubbled
                    throw (Error) t ; }
                else if( t instanceof RuntimeException ) {
                    // Unchecked exceptions are bubbled
                    throw (RuntimeException) t ; }
                else {
                    // Checked exceptions indicate a precondition violation.
                    throw new IllegalArgumentException(t) ; } }
            catch( NoSuchMethodException e ) {
                throw new AssertionError(null, e) ; } }
    }
    
  • 12

    上面的例子是有效的,非常Java . 但是,我将如何解决OP关于如何处理该回报的问题:

    public Object getClone(Cloneable a) throws CloneNotSupportedException {
        return a.clone();
    }
    

    检查 a 以查看它是否为空没有任何好处 . 它将进入NPE . 打印堆栈跟踪也没有帮助 . 无论处理的位置如何,堆栈跟踪都是相同的 .

    使用无用的空测试和无用的异常处理来获取代码没有任何好处 . 通过删除垃圾,返回问题没有实际意义 .

    (请注意,OP包含了异常处理中的错误;这就是为什么需要返回的原因.POP不会出错我建议的方法 . )

  • 132

    有一个return语句只是为了满足语法不良的做法吗?

    正如其他人所提到的,在你的情况下,这实际上并不适用 .

    但是,要回答这个问题,Lint类型的程序肯定没有想到它!我已经看到两个不同的人在切换声明中对此进行斗争 .

    switch (var)
       {
         case A:
           break;
         default:
           return;
           break;    // Unreachable code.  Coding standard violation?
       }
    

    有人抱怨说,休息是违反编码标准的 . 另一个抱怨说 having 它是一个,因为它是无法访问的代码 .

    我注意到了这一点,因为两个不同的程序员不断重新检查代码,添加了break然后删除然后添加然后删除,这取决于他们当天运行的代码分析器 .

    如果你最终遇到这种情况,请选择一个并对异常进行评论,这是你自己展示的好形式 . 这是最好和最重要的外卖 .

  • 0

    它不仅仅是“满足语法” . 语言的语义要求是每个代码路径都会导致返回或抛出 . 此代码不符合 . 如果发现异常,则需要返回以下内容 .

    没有'糟糕的做法',或者一般来说是满足编译器 .

    在任何情况下,无论是语法还是语义,您都没有任何选择 .

  • 6

    我会改写这个以便在最后得到回报 . 伪代码:

    if a == null throw ...
    // else not needed, if this is reached, a is not null
    Object b
    try {
      b = a.clone
    }
    catch ...
    
    return b
    
  • 2

    没有人提到这个,所以这里是:

    public static final Object ERROR_OBJECT = ...
    
    //...
    
    public Object getClone(Cloneable a) throws TotallyFooException {
    Object ret;
    
    if (a == null) 
        throw new TotallyFooException();
    
    //no need for else here
    try {
        ret = a.clone();
    } catch (CloneNotSupportedException e) {
        e.printStackTrace();
        //something went wrong! ERROR_OBJECT could also be null
        ret = ERROR_OBJECT; 
    }
    
    return ret;
    
    }
    

    出于这个原因,我不喜欢 try 块内的 return .

  • 5

    返回null;是必要的,因为可能会捕获异常,但是在这种情况下,因为我们已经检查它是否为null(并假设我们知道我们调用的类支持克隆)所以我们知道try语句永远不会失败 .

    如果您知道所涉及的输入的详细信息,您知道 try 语句永远不会失败,那么拥有它的重点是什么?如果您确定事情总是会成功的话,请避免使用 try (尽管您可以在代码库的整个生命周期内完全确定这种情况很少见) .

    无论如何,遗憾的是编译器不是读者 . 它看到了函数及其输入,并且根据它所拥有的信息,它需要在底部有 return 语句 .

    在最后添加额外的return语句只是为了满足语法并避免编译错误(使用注释解释它不会达到)是不好的做法,或者是否有更好的方法来编写这样的代码以便不需要额外的退货声明?

    恰恰相反,我很好地避免了任何编译器警告,例如,即使这会花费另一行代码 . 这里不要过分担心行数 . 通过测试确定功能的可靠性,然后继续 . 只是假装你可以省略 return 语句,想象一年之后再回到那段代码,然后尝试判断底部的语句是否会引起更多的混淆,而不是详细说明为什么因为假设而被省略的细节你可以做一下输入参数 . 最有可能的是 return 声明会更容易处理 .

    那就是说,特别是关于这部分:

    try {
        return a.clone();
    } catch (CloneNotSupportedException e) {
       e.printStackTrace();
    }
    ...
    //cant be reached, in for syntax
    return null;
    

    我认为这里的异常处理思路有些奇怪 . 您通常希望在您可以做出有意义的响应的站点上吞下异常 .

    您可以将 try/catch 视为一种交易机制 . try 进行这些更改,如果它们失败并且我们分支进入 catch 块,作为回滚和恢复过程的一部分,执行此操作(在 catch 块中的任何内容)作为响应 .

    在这种情况下,仅打印堆栈跟踪然后被强制返回null并不完全是事务/恢复思维模式 . 代码将错误处理责任转移到调用 getClone 的所有代码,以手动检查故障 . 您可能更喜欢捕获 CloneNotSupportedException 并将其转换为另一种更有意义的异常形式并将其抛出,但您不希望简单地吞下异常并在此情况下返回null,因为这不像事务恢复站点 .

    你最终会将责任泄露给调用者来手动检查并以这种方式处理失败,当抛出异常时会避免这种情况 .

    它's like if you load a file, that'是高级交易 . 那里你可能有一个 try/catch . 在 trying 加载文件的过程中,您可能会克隆对象 . 如果在这个高级操作(加载文件)中的任何地方出现故障,您通常希望将异常一直抛回到此顶级事务 try/catch 块,以便您可以从加载文件失败中正常恢复(无论是否它想要在像这样的一些细粒度的地方吞下一个异常,然后返回一个null,例如,因为那会破坏很多异常的 Value 和目的 . 相反,我们希望将异常一直传播回一个我们可以有意义地处理它的网站 .

  • 7

    您的示例并不理想,如上一段所述:

    在最后添加额外的return语句只是为了满足语法并避免编译错误(使用注释解释它不会达到)是不好的做法,或者是否有更好的方法来编写这样的代码以便不需要额外的退货声明?

    一个更好的例子是克隆本身的实现:

    public class A implements Cloneable {
          public Object clone() {
               try {
                   return super.clone() ;
               } catch (CloneNotSupportedException e) {
                   throw new InternalError(e) ; // vm bug.
               }
          }
     }
    

    这里永远不应该输入catch子句 . 语法仍然需要抛出一些东西或返回一个值 . 由于返回某些内容没有意义,因此_2639799用于指示严重的VM条件 .

相关问题