在Java中真正的解决scheme:从2个stringparsing2个数字,然后返回它们的总和
相当愚蠢的问题。 给定代码:
public static int sum(String a, String b) /* throws? WHAT? */ { int x = Integer.parseInt(a); // throws NumberFormatException int y = Integer.parseInt(b); // throws NumberFormatException return x + y; }
你能告诉我这个Java是不是很好? 我在说的是, NumberFormatException
是一个未经检查的exception。 您不必将其指定为sum()
签名的一部分。 此外,据我所知,未检查的exception的想法只是表示程序的实现是不正确的,更重要的是,捕获未检查的exception是一个坏主意,因为它就像在运行时修复坏的程序 。
请有人澄清是否:
- 我应该指定
NumberFormatException
作为方法签名的一部分。 - 我应该定义自己的检查exception(
BadDataException
),处理方法内的NumberFormatException
并将其重新抛出为BadDataException
。 - 我应该定义自己的检查exception(
BadDataException
),以正则expression式的方式validation两个string,如果不匹配,则抛出BadDataException
。 - 你的想法?
更新 :
想象一下,这不是一个开源框架,你应该使用一些原因。 你看方法的签名,并认为 – “好吧,它永远不会抛出”。 那么,有一天,你有一个例外。 这是正常的吗?
更新2 :
有一些评论说我的sum(String, String)
是一个糟糕的devise。 我绝对同意,但对于那些认为如果我们有良好的devise,原来的问题将永远不会出现的人,这里有一个额外的问题:
问题的定义是这样的:你有一个数据源,其中数字存储为String
。 这个来源可能是XML文件,网页,带有2个编辑框的桌面窗口,无论如何。
你的目标是实现这2个String
的逻辑,将它们转换为int
并显示消息框“总和为xxx”。
不pipe你用什么方法来devise/实现这个,你都会拥有这两个内在的function :
- 将
String
转换为int
- 一个你添加2个
int
的地方
我原来的post的主要问题是:
Integer.parseInt()
需要传递正确的string。 每当你传递一个错误的string ,这意味着你的程序是不正确的(不是“ 你的用户是一个白痴”)。 你需要实现一段代码,一方面你必须使用Integer.parseInt()和MUST语义 ,另一方面你需要确定input不正确的情况 – SHOULD语义 。
所以,简单地说:如果我只有MUST库,我该如何实现SHOULD语义 。
这是一个很好的问题。 我希望更多的人会考虑这样的事情。
恕我直言,抛出未经检查的例外是可以接受的,如果你已经通过垃圾参数。
一般来说,您不应该抛出BadDataException
因为您不应该使用Exceptions来控制程序stream。 例外是例外。 你的方法的调用者可以在他们调用它之前知道它们的string是否是数字,所以传递垃圾是可以避免的,因此可以被认为是编程错误 ,这意味着可以抛出未经检查的exception。
关于声明throws NumberFormatException
– 这是没有用的,因为NumberFormatException被取消选中,很less会注意到。 但是,IDE的可以利用它提供正确的try/catch
包。 一个好的select是使用javadoc,例如:
/** * Adds two string numbers * @param a * @param b * @return * @throws NumberFormatException if either of a or b is not an integer */ public static int sum(String a, String b) throws NumberFormatException { int x = Integer.parseInt(a); int y = Integer.parseInt(b); return x + y; }
编辑 :
评论者提出了有效的观点。 你需要考虑如何使用它和你的应用程序的整体devise。
如果这个方法会被遍地使用,所有的调用者处理问题都是很重要的,那么声明这个方法是抛出一个检查的exception(强制调用者处理问题),但是使用try/catch
块来混淆代码。
另一方面,如果我们正在使用这个方法来处理我们信任的数据,那么就像上面那样声明它,因为它不会被爆炸,并且避免了本质上不必要的try/catch
块的代码混乱。
在我看来,最好尽可能地处理exception逻辑。 所以我更喜欢签名
public static int sum(int a, int b);
用你的方法签名,我不会改变任何东西。 无论你是谁
- 以编程方式使用不正确的值,您可以在那里validation您的生产者algorithm
- 或从例如用户input发送值,在这种情况下,该模块应该执行validation
因此,在这种情况下,exception处理成为文档问题。
Number 4.如给出,这个方法不应该把string作为参数,它应该是整数。 在这种情况下(由于java包装而不是溢出),不存在exception的可能性。
x = sum(Integer.parseInt(a), Integer.parseInt(b))
对于x = sum(a, b)
是什么意思x = sum(a, b)
您希望发生的exception尽可能接近源(input)。
至于选项1-3,你没有定义一个exception,因为你期望你的调用者假设否则你的代码不会失败,你定义了一个exception来定义在已知失败条件下发生的exception,这对你的方法是唯一的。 也就是说,如果你有一个方法是另一个对象的包装,它会抛出一个exception,然后传递。 只有当你的方法的exception是唯一的,你应该抛出一个自定义的exception(frex,在你的例子中,如果sum只是返回肯定的结果,然后检查,并抛出一个exception将是适当的,如果另一方面Java抛出一个溢出exception,而不是包装,然后你会传递,不要在你的签名中定义它,重命名它,或者吃它)。
更新以回应问题的更新:
所以,简单地说:如果我只有MUST库,我该如何实现SHOULD语义。
解决scheme是包装MUST库,并返回SHOULD值。 在这种情况下,返回一个Integer的函数。 编写一个接受一个string并返回一个Integer对象的函数 – 它可以工作,或者返回null(像guava的Ints.tryParse)。 你的validation是否与你的操作分开,你的操作应该采取整理。 无效input时,无论您的操作是否被默认值调用,或者您做了其他操作,都将取决于您的规格 – 我可以说的是,做决定的地方不太可能在操作中方法。
1.我应该指定NumberFormatException作为方法签名的一部分。
我想是这样。 这是一个很好的文档。
2.我应该定义我自己的检查exception(BadDataException),处理方法内的NumberFormatException并将其重新抛出为BadDataException。
有时候是。 在某些情况下,检查的exception被认为是更好的,但与他们合作是相当的PITA。 这就是为什么许多框架(如Hibernate)只使用运行时exception的原因。
3.我应该定义自己的检查exception(BadDataException),以正则expression式的方式validation两个string,如果不匹配,则抛出BadDataException。
决不。 更多的工作,更less的速度(除非你期望抛出exception是一个规则),并没有收益。
你的想法?
一个都没有。
Nr 4。
我想我根本不会改变这个方法。 我会尝试捕捉堆栈跟踪中的调用方法或更高的地方,在这里我可以从exception中正常恢复业务逻辑。
我不敢肯定,我认为它是过度的。
假设你正在编写的东西将被其他人使用(比如作为一个API),那么你应该使用1,NumberFormatException专门用于传达这种exception的目的,应该使用。
-
首先你需要问自己,我的方法的用户是否需要担心input错误的数据,或者是否希望他input正确的数据(在这种情况下是string)。 这个期望也被称为合同devise。
-
3.是的,你可能应该定义BadDataException,或者甚至更好地使用一些像NumberFormatException这样的删除操作,而不是标准的消息显示。 在方法中捕获NumberFormatException并将其重新抛出,而不要忘记包含原始堆栈跟踪。
-
这取决于情况,我可能会重新抛出NumberFormatException与一些额外的信息。 而且还必须有一个javadoc解释什么是
String a, String b
的期望值
取决于你所处的场景。
案例1.它总是你谁debugging的代码,没有其他人和exception不会造成不良的用户体验
抛出默认的NumberFormatException
情况2:代码应该是非常可维护和可以理解的
定义你自己的exception,并添加更多的数据进行debugging,同时抛出它。
你不需要正则expression式检查,它会去坏的input例外。
如果这是一个生产级别的代码,我的想法是定义多个自定义exception,如
- 数字格式exception
- 溢出exception
- 空例外等…
并处理所有这些分开
- 您可能会这样做,以清楚地表明,这可能会发生错误的input。 它可能会帮助使用您的代码的人记住处理这种情况。 更具体地说,你清楚地表明你自己并没有在代码中处理它,或者返回一些具体的值。 当然,JavaDoc也应该清楚这一点。
- 只有当你想强制调用者处理一个检查的exception。
- 这似乎是矫枉过正。 依靠parsing来检测错误的input。
总体而言,NumberFormaException未被选中,因为预期会提供正确的可parsinginput。 inputvalidation是你应该处理的。 但是,实际parsinginput是最简单的方法。 您可以简单地保留原来的方法,并在文档中警告预期有正确的input,并且任何调用您的函数的人都应该在使用之前validation两个input。
任何exception的行为都应该在文档中予以澄清。 要么应该声明这个方法返回一个特殊的值,以防失败的情况(如null
,通过改变返回types为Integer
)或者case 1应该被使用。 如果通过其他方法确保正确的string,在方法的签名中明确地使用户可以忽略它,但是显然这种方法本身并不能处理这种失败。
回答您的更新问题。
是的,获得“惊喜”例外是完全正常的。 想想所有运行时错误,当编程新手。
eg ArrayIndexOutofBound
每个循环也是一个常见的意外exception。
ConcurrentModificationException or something like that
虽然我同意答案,运行时exception应该被允许渗透,从devise和可用性的angular度来看,将它包装成IllegalArgumentException是一个好主意,而不是把它抛出NumberFormatException。 这就使得你的方法的合同更加清晰,从而宣布了一个非法的论点,因此引发了一个例外。
关于这个问题的更新“想象一下,它不是一个开放源代码的框架,你应该使用一些原因,你看方法的签名,并思考 – ”好的,它永远不会抛出“,然后,有一天,你有一个例外这是正常的吗?“ 你的方法的javadoc应该总是泄漏你的方法的行为(前后约束)。 说收集界面的行,如果不允许null,javadoc说,将抛出一个空指针exception,虽然它不是方法签名的一部分。
正如你在谈论良好的Java实践,在我看来,总是更好
-
处理未经检查的exception,然后分析它,并通过自定义的未经检查的exception。
-
同时抛出自定义的未经检查的exception,您可以添加客户端可以理解的exception消息,并打印原始exception的堆栈跟踪
-
不需要声明自定义exception为“抛出”,因为它是未经检查的。
-
这样你就不会违反使用什么未经检查的exception,同时代码的客户端很容易理解exception的原因和解决scheme。
-
同时在java-doc中正确logging是一个很好的做法,并有很大的帮助。
我认为这取决于你的目的,但我会至lesslogging下来:
/** * @return the sum (as an int) of the two strings * @throws NumberFormatException if either string can't be converted to an Integer */ public static int sum(String a, String b) int x = Integer.parseInt(a); int y = Integer.parseInt(b); return x + y; }
或者,从java.lang.Integer类的Java源代码中获取一个页面:
public static int parseInt(java.lang.String string) throws java.lang.NumberFormatException;
Google的“Guava”库或Apache的“Validator”库 ( 比较 )实现的inputvalidation模式如何?
根据我的经验,在函数开始时validation函数的参数并在适当情况下抛出exception是一种很好的做法。
另外,我认为这个问题大部分是与语言无关的。 这里的“良好实践”适用于所有具有可以采用可能有效或无效参数的函数的语言。
我认为你的第一句“很愚蠢的问题”是非常相关的。 为什么你会首先用这个签名来写一个方法? 这两个string是否合理? 如果调用方法想要总结两个string,调用方法的责任是确保它们是有效的整数并在调用方法之前将其转换。
在这个例子中,如果调用方法不能将两个string转换为一个int,它可以做几件事情。 这真的取决于这个总和发生在什么层。 我假设string转换将非常接近前端代码(如果正确完成),那么情况1将是最有可能的:
- 设置错误消息并停止处理或redirect到错误页面
- 返回false(即将它放到其他对象中,不需要返回)
- 按照您的build议抛出一些BadDataException,但除非这两个数字的总和非常重要,否则这是过度的,如上所述,这可能是糟糕的devise,因为这意味着转换是在错误的地方完成的
这个问题有很多有趣的答案。 但我仍然想补充一点:
对于stringparsing,我总是喜欢使用“正则expression式”。 java.util.regex包在那里帮助我们。 所以我最终会得到这样的事情,从来没有抛出任何exception。 如果我想捕捉到一些错误,那么返回一个特殊的值就取决于我:
import java.util.regex.Pattern; import java.util.regex.Matcher; public static int sum(String a, String b) { final String REGEX = "\\d"; // a single digit Pattern pattern = Pattern.compile(REGEX); Matcher matcher = pattern.matcher(a); if (matcher.find()) { x = Integer.matcher.group(); } Matcher matcher = pattern.matcher(b); if (matcher.find()) { y = Integer.matcher.group(); } return x + y; }
可以看到,代码只是稍微长一些,但我们可以处理我们想要的(并为x和y设置默认值,控制else子句发生了什么等等)。我们甚至可以写一个更一般的转换例程,我们可以传递string,默认返回值,REGEX代码编译,错误消息扔,…
希望这是有用的。
警告:我无法testing这个代码,所以请原谅最终的语法问题。
您面临这个问题是因为您让用户错误传播太深入应用程序的核心,部分原因是您滥用Java数据types。
您应该在用户inputvalidation和业务逻辑之间有一个更明确的分隔,使用正确的数据types,这个问题本身就会消失。
事实是Integer.parseInt()
的语义是已知的 – 它是parsing有效整数的主要目的。 您缺less一个明确的用户inputvalidation/parsing步骤。
- 我应该总是在`except`语句中指定一个exceptiontypes吗?
- 为什么在C#中捕获并重新抛出exception?
- 谁删除在“新”操作期间分配的内存,在构造函数中有exception?
- 如何沉默“sys.excepthook失踪”错误?
- 没有抛出VirtualMachineError保证
- Windows / C ++:是否有可能findexception被抛出的代码行有“Exception Offset”
- 最佳做法:从属性抛出exception
- Kill(),Environment.Exit(0)或this.Shutdown()
- 更好地“尝试”一些东西,并捕捉exception或testing是否可能首先避免exception?