这是对的吗?
示例代码:
void foo(E param){
if(param == null){
// do this
}else{
// do that
}
//...
}
首先,我阅读了一些帖子,其中讨论允许变量为空的人增加了软件Best explanation for languages without null的复杂性,所以我想做出努力并忘记这一点,试图回答这个问题。 / p>
思想(我的想法)
作为一个初学者,我的想法可能是错的,但我希望你帮助我清除它。
我认为检查变量是否为null 是正确的,具体取决于该变量的上下文,让我用一个例子来解释:
使用不当:
我们的项目实现了Command模式,我们有一套指令。然后我们在控制台中写一条指令,我们必须解释它。我们有一个解释器类,它试图解析它,所以如果解释器找不到合适的指令,它将返回null。
代码示例:
public class Interpreter {
public static final String LINE_SEPARATOR = System.getProperty("line.separator");
private static final List<Instruction> listInstructions = loadInstructions();
/**
* Generates a new instruction according to the user input
* @param line - A string with the user input
* @return The instruction read from the given line. If the instruction is not correct, then it returns null.
*/
public static Instruction generateInstruction(String line) throws WrongInstructionFormatException
{
Instruction instru = null;
Iterator<Instruction> it = listInstructions.iterator();
int i = 0;
while(it.hasNext() && i <9 && instru == null)
{
try
{
instru = it.next().parse(line);
}
catch(WrongInstructionFormatException e)
{
}
finally
{
i++;
}
}
return instru;
}
//...
public class MainClass
{
/**
* MainFunction
*/
public void mainFuction()
{
Instruction instru;
while(true)
{
instru = Interpreter.generateInstruction(orden);
if(instru == null) <------
// Error.
else
instru.execute();
}
}
}
在这种情况下,我认为我们正在实现这个错误,因为解释器不能返回null但是异常,因为在我看来此上下文中的变量永远不能为空。 我希望我很清楚,如果不是,那么我想稍后澄清您的意见
正确使用:(已编辑)
我们项目的主要类有一组属性,可以从中实例化或不实例化。根据我们是否实例化了一个,两个或两个都没有一组函数来执行一个,两个或不执行任何操作。
public class MainClass extends Observable
{
private A a;
private B b; // This attribute allows you to start making a log in a txt file
private C c; // This attribute allows you to start making a log in a DB
public MainClass(A a){ //....}
public MainClass(A a, B b) { // .....}
public MainClass(A a, C c) { // .....}
public MainClass(A a, B b, C c){ //.....}
public void log(String update)
{
if(this.b != null) <-----
// Save the update in the txt file
if(this.c != null) <-----
// Save the update in a db row
}
}
嗯,为什么这是对的?我认为是正确的,因为属性可能具有空值而不是前一种情况。
随着这开始讨论,如果他们有同样的疑问,我希望能帮助其他人。
答案 0 :(得分:3)
关于空值的建议不是检查它们,而是不生成或允许它们。显然,如果程序中有空值,则需要检查它们。
在你的第二个例子中,遵循建议的方法是找到一种方法来实现你的类,使它没有可空的字段。为什么该类具有可选属性?它对这些属性有什么作用?有没有办法构建类(可能通过将其分成多个类),以便它没有可选的属性?
在第二个示例中,您在if
测试中使用了属性的无效性。面向对象设计中的标准操作是用多态替换条件。你能试试吗?
编辑:好的,这是我如何使用多态解决第二种情况。我删除了字段a
,因为在这种情况下它并不相关。
public class MainClass extends Observable {
private final Collection<? extends LogDestination> logDestinations;
public MainClass() {
logDestinations = Collections.emptySet();
}
public MainClass(B b) {
logDestinations = Collections.singleton(new TextFileLog(b));
}
public MainClass(C c) {
logDestinations = Collections.singleton(new DatabaseLog(c));
}
public MainClass(B b, C c) {
logDestinations = Arrays.asList(new TextFileLog(b), new DatabaseLog(c));
}
public void log(String update) {
for (LogDestination logDestination : logDestinations) {
logDestination.log(update);
}
}
}
interface LogDestination {
public void log(String update);
}
class TextFileLog implements LogDestination {
private final B b;
public TextFileLog(B b) {
this.b = b;
}
@Override
public void log(String update) {
// Save the update in the txt file
}
}
class DatabaseLog implements LogDestination {
private final C c;
public DatabaseLog(C c) {
this.c = c;
}
@Override
public void log(String update) {
// Save the update in a db row
}
}
用抽象的术语来说,这样做是将逻辑中唯一的替代方法一直推到构造函数中,形式是选择要创建的LogDestination
s。一旦创建它们,就可以完全统一地处理它们,并使用多态性将一般调用分派给特定方法。因为设置对象的不同方式有不同的构造函数,所以根本不需要任何条件逻辑。
答案 1 :(得分:2)
如果是预期和/或可恢复的情况,抛出Exception
vs返回null
几乎完全是一种风格问题。你所提出的论点是很好的区分,如果它们对你有意义,那么我就说去吧。
当然,这并不是说您应该使用Exceptions作为首选流量控制工具,但在您所描述的情况下,它听起来非常合理。
答案 2 :(得分:1)
我有一个超过2000个课程的程序。假设每个类约有20个函数,那就是40,000个函数。他们中的很多人每行只有几行代码(例如:get / set functions)。如果每个函数都检查了空参数等,那么很多代码只用于验证(对于许多处理的异常,可以使用类似的参数)。此外,如果我只检查空值,我不妨进行完全验证以确保(javadoc)合同得到满足。例如,迭代传递给函数的对象列表,检查空值,范围问题等。请注意,该方法会出现性能问题。
相反,除了在javadoc(按合同设计)中指定之外,我更喜欢有一个适用于整个项目的商定政策,即不允许任何函数为参数赋值,或者返回值为null。在调用我的函数之前,调用者有责任遵守(javadoc)合同并进行任何验证检查。但是,我会考虑对带有多个参数的构造函数进行验证检查(以确保不创建不完整的对象),以及一些在处理输入参数方面过于复杂的函数。请注意,我的函数的直接调用者不必在调用我的函数之前进行验证检查。相反,在调用链的某个地方至少有一个其他功能需要这样做。 注意:我在网上看到关于处理这一切的正确方法存在很多争论。