有一个返回语句只是为了满足语法不好的做法?
考虑下面的代码:
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;
是必要的,因为一个exception可能被捕获,但是在这种情况下,因为我们已经检查过它是否为空(假设我们知道我们调用的类支持克隆),所以我们知道try语句永远不会失败。
为了满足语法和避免编译错误(有一个解释它不会被达成的注释),还是有更好的方法来编写类似这样的代码是不好的做法,返回语句是不必要的?
没有额外的返回声明的更清晰的方式如下。 我也不会捕获CloneNotSupportedException
,但让它去调用者。
if (a != null) { try { return a.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } } throw new TotallyFooException();
几乎总是有可能摆脱这个命令,最终得到比你最初的语法更直接的语法。
这绝对是可以达成的。 请注意,您只是在catch
子句中打印堆栈跟踪。
在a != null
的情况下, 将会有一个exception,将return null
。 你可以删除这个语句,并用throw new TotallyFooException();
replace它throw new TotallyFooException();
。
一般情况下* ,如果null
是一个方法的有效结果(即用户期望它,它意味着什么),那么返回它作为“未find数据”或发生exception的信号不是一个好主意。 否则,我看不出有什么问题,为什么你不应该返回null
。
以Scanner#ioException
方法为例:
返回此Scanner底层Readable所抛出的
IOException
。 如果不存在这样的exception,则此方法返回 null 。
在这种情况下,返回值null
具有明确的含义,当我使用方法时,我可以肯定,我只有null
因为没有这样的exception,而不是因为方法试图做一些事情,失败。
*请注意,即使意义不明确,有时您也希望返回null
。 例如HashMap#get
:
null的返回值不一定表示映射不包含该键的映射; 地图也可能明确地将密钥映射到 空值 。
containsKey
操作可以用来区分这两种情况。
在这种情况下, null
可以表示find并返回null
值 ,或者hashmap不包含请求的键。
为了满足语法和避免编译错误,在最后join额外的返回语句是不好的做法(注释解释不了)
我认为return null
是一个不可达分支的总站不好的做法。 抛出一个RuntimeException
( AssertionError
也是可以接受的)是更好的办法,因为到了那一行,出现了一些错误,应用程序处于未知状态。 大部分像这样(就像上面那样)是因为开发者已经错过了一些东西(对象可以是非null和不可复制的)。
我可能不会使用InternalError
除非我非常确定代码是不可访问的(例如,在System.exit()
),因为我比VM更可能犯错。
我只会使用一个自定义的exception(如TotallyFooException
),如果得到的“不可达线”意味着与其他任何地方抛出该exception相同的东西。
您遇到了CloneNotSupportedException
,这意味着您的代码可以处理它。 但是当你捕捉到它之后,当你到达函数结束时,你几乎不知道该怎么做,这意味着你无法处理它。 所以你是对的,在这种情况下,它是一种代码味道,在我看来,意味着你不应该抓到CloneNotSupportedException
。
我宁愿使用Objects.requireNonNull()
来检查参数a是否不为null。 所以当你读取代码的时候,参数不应该是null。
而为了避免检查exception,我将抛出CloneNotSupportedException
作为一个RuntimeException
。
对于这两个你可以添加很好的文字,意图为什么不应该发生这种情况。
public Object getClone(Object a) { Objects.requireNonNull(a); try { return a.clone(); } catch (CloneNotSupportedException e) { throw new IllegalArgumentException(e); } }
在这种情况下,我会写
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语句永远不会失败”,但是你仍然不厌其烦地写一个语句e.printStackTrace();
你声称永远不会被执行。 为什么?
也许你的信念并不那么坚定。 这是好的(在我看来),因为你的信念不是基于你所写的代码,而是期望你的客户不会违反前提条件。 最好是防守性地编程公共方法。
顺便说一下,你的代码不会为我编译。 即使a
的types是Cloneable
也不能调用a.clone()
。 至lessEclipse的编译器这样说。 expression式a.clone()
给出错误
方法clone()对于Cloneabletypes是未定义的
我会为你的具体情况做什么
public Object getClone(PubliclyCloneable a) throws TotallyFooException { if (a == null) { throw new TotallyFooException(); } else { return a.clone(); } }
其中PubliclyCloneable
由其定义
interface PubliclyCloneable { public Object clone() ; }
或者,如果您确实需要参数types为Cloneable
,则至less编译以下内容。
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) ; } } }
上面的例子是有效的,非常Java。 但是,我将如何解决OP如何处理这一回报的问题:
public Object getClone(Cloneable a) throws CloneNotSupportedException { return a.clone(); }
检查a
是否为空是没有好处的。 这是NPE。 打印堆栈跟踪也没有帮助。 不pipe在哪里处理,堆栈跟踪都是一样的。
用无用的空testing和无用的exception处理来代替代码是没有好处的。 通过删除垃圾,返回问题是没有意义的。
(请注意,OP在exception处理中包含了一个bug,这就是为什么需要返回的原因,OP不会错误的使用我提出的方法。)
有一个返回语句只是为了满足语法不好的做法?
正如其他人所提到的,在你的情况下,这实际上并不适用。
为了回答这个问题,虽然Linttypes的程序肯定没有想出来! 我已经看到两个不同的在一个switch语句中对此进行了争论。
switch (var) { case A: break; default: return; break; // Unreachable code. Coding standard violation? }
一个人抱怨说没有rest是违反编码标准的。 另一个抱怨说,因为它是无法访问的代码,所以它是一个。
我注意到这一点,因为两个不同的程序员不断重新检查代码中的添加和删除,然后添加和删除,取决于他们哪天运行的代码分析器。
如果你在这种情况下结束,select一个,并评论exception,这是你自己展现的好forms。 这是最好的和最重要的外卖。
这不是“只是为了满足语法”。 每个代码path导致返回或抛出是语言的语义要求。 此代码不符合。 如果发生exception,则需要执行以下返回。
没有“不好的做法”,或关于一般的编译器。
无论如何,无论是语法还是语义,你都没有任何select。
我会重写这个最后的回报。 伪代码:
if a == null throw ... // else not needed, if this is reached, a is not null Object b try { b = a.clone } catch ... return b
没有人提到这一点,所以在这里:
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; }
我不喜欢在这个问题里面回答这个问题。
返回null; 是必要的,因为一个exception可能被捕获,但是在这种情况下,因为我们已经检查过它是否为空(假设我们知道我们调用的类支持克隆),所以我们知道try语句永远不会失败。
如果你知道有关input的细节,你知道try
语句永远不会失败,那有什么意义呢? 避免try
如果你知道事情总是会成功的(尽pipe你可以绝对肯定你的代码库的整个生命周期)。
无论如何,编译器不幸的不是一个心灵读者。 它看到函数及其input,并给出它所具有的信息,它需要在底部return
声明。
为了满足语法和避免编译错误(有一个解释它不会被达成的注释),还是有更好的方法来编写类似这样的代码是不好的做法,返回语句是不必要的?
恰恰相反,我build议避免任何编译器警告是一个很好的做法,例如,即使这会花费另一行代码。 在这里不要太在意行数。 通过testingbuild立function的可靠性,然后继续。 假设你可以省略return
声明,想象一年后再回到这个代码,然后试图确定底部的return
声明是否会引起更多的混淆,而不是一些评论,详细说明了为什么它被忽略的细节,因为假设你可以制作input参数。 return
声明很可能会更容易处理。
这就是说,具体关于这个部分:
try { return a.clone(); } catch (CloneNotSupportedException e) { e.printStackTrace(); } ... //cant be reached, in for syntax return null;
我觉得在这里处理exception的心态有点奇怪。 你通常想要在你有一些有意义的事情的地方吞下例外,你可以做出回应。
你可以把try/catch
看作一个交易机制。 try
进行这些更改,如果它们失败,并且我们分支到catch
块中,则作为回滚和恢复过程的一部分做出响应(无论在catch
块中)作为响应。
在这种情况下,仅仅打印一个堆栈跟踪,然后被迫返回空值不完全是一个事务/恢复的心态。 代码将error handling责任传递给调用getClone
所有代码,以手动检查失败。 您可能更喜欢捕获CloneNotSupportedException
并将其转换为另一种更有意义的exceptionforms并抛出exception,但您不希望简单地吞下exception并在此情况下返回null,因为这不像事务恢复站点。
您最终会将责任泄漏给调用者,以便以这种方式手动检查和处理失败,抛出exception将会避免这种情况。
这就像是如果你加载一个文件,这是高层次的交易。 你可能有一个try/catch
那里。 在trying
加载文件的过程中,您可能会克隆对象。 如果在这个高级操作(加载文件)中的任何地方出现故障,通常希望将这些exception抛出回到这个顶级事务try/catch
块,以便在加载文件时从错误中正常恢复(无论是由于克隆或其他方面的错误)。 所以我们通常不想在这样的细粒度的地方吞下一个exception,然后返回一个null,例如,因为这样会打败exception的很多价值和目的。 相反,我们希望将exception传播回一个可以有意义处理的地方。
你的例子是不是很好的说明你的问题,如在最后一段所述:
为了满足语法和避免编译错误(有一个解释它不会被达成的注释),还是有更好的方法来编写类似这样的代码是不好的做法,返回语句是不必要的?
一个更好的例子是克隆本身的实现:
public class A implements Cloneable { public Object clone() { try { return super.clone() ; } catch (CloneNotSupportedException e) { throw new InternalError(e) ; // vm bug. } } }
这里不应该inputcatch子句。 语法或者需要抛出一些东西或者返回一个值。 由于返回的东西没有任何意义,因此使用InternalError
来指示严重的VM情况。