避免空检查?

时间:2013-06-28 21:16:59

标签: java nullpointerexception

我想知道是否有可能在Java中“避免”空检查,请从此代码中获取示例:

@Override
public List<AccountBean> search(AccountConstraint... c) {
    if (c.length == 0) {
        throw new IllegalArgumentException("dao.AccountDAO.search: c.length == 0");
    }
    try {
        List<AccountBean> beans = new ArrayList<>();
        for (AccountConstraint ac : c) {
            Builder builder = new QueryBuilder.Builder("SELECT * FROM accounts");
            if (ac.getAccountId() != null) {
                builder.clause("accountId >= " + ac.getAccountId().getMin() + " AND accountId <= " + ac.getAccountId().getMax());
            }
            if (ac.getUsername() != null) {
                builder.clause("username = \"" + ac.getUsername() + "\"");
            }
            if (ac.getPassword() != null) {
                builder.clause("password = \"" + ac.getPassword() + "\"");
            }
            if (ac.getEmail() != null) {
                builder.clause("email = \"" + ac.getEmail() + "\"");
            }
            PreparedStatement ps = connection.prepareStatement(builder.build().getQuery());
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                beans.add(new AccountBean(rs));
            }
        }
        return beans;
    } catch (SQLException ex) {
        throw new RuntimeException(ex);
    }
}

它必须为!= null检查4次,否则代码会失败。

如果没有NullPointerException,是否可以将if (object != null)语句转换为执行的单行?如果有异常,则应该忽略该行。

我不是在谈论一般语言功能,我在讨论的功能只有在你明确决定这样做时才会被提交。

例如:NullCheck(builder.clause("username = \"" + ac.getUsername() + "\""));将是建议代码的一小部分。

Java中是否可以这样?

如果不可能,Java 8中是否可以直接在方法中使用方法(空洞)?

那么这样的代码实际上可以工作吗?

public static NullCheck(Void void) {
    try {
        void.execute();
    }
    catch (NullPointerException e) {
        //ignore
    }
}

我知道我可以将该方法放在它自己的类中,该类扩展了一个具有方法execute()的接口,然后传递该类,但这样做会破坏删除空检查的目的或任何会更复杂。

问候。

警告:我在这里使用PreparedStatement的方式很容易出现SQL注入。请勿重复使用此代码。

5 个答案:

答案 0 :(得分:3)

您可以通过假设这些方法不返回空值来避免这些检查。

你怎么能这么想?通过AccountConstraint的规范说明。然后AccountConstraint中的代码负责确保值不为空,而不是负责处理空值的search方法。您可能需要更改AccountConstraint的设计才能执行此操作。

如果你的假设错了会发生什么?也就是说,如果AccountConstraint是错误的。将抛出异常,这是您没想到的。但是当你遇到错误时会发生这种情况:抛出意外的异常。调试代码很容易,因为堆栈跟踪将显示AccountConstraint哪个方法返回无效的空值。

答案 1 :(得分:2)

是和否。

解决null问题有两种方法:

特殊操作员喜欢Groovy中的安全导航操作员。如果x.y引发NullPointerException x?.y只返回null。由于Java不允许创建新的运算符,因此无法在Java中执行此操作。这样的运营商考虑用于JDK8,但在哪里被删除。如果你想要这样的东西,请切换到Groovy或具有此功能的许多其他语言之一。

特殊类许多语言都有一个特殊的界面来表示可能是null的值。在Scala中,它被称为Option。 Option有两个实现:None + Some。无替代null。无论何时你想对值进行某些操作,都不要直接使用它,而是使用函数作为参数在Option上调用map。如果它实际上是一个无,没有任何反应,你只需返回无。如果它是Some,则函数将在值上执行,并且您将获得带有结果的Option。这样您就可以随时使用选项,而无需担心null s。

实际上它现在很特别,所以你可以用Java自己创建这样一个类。问题是,Java没有函数,所以你必须使用匿名类。这使整个事情变得非常麻烦,只是一个理论选择。

JDK8有一个Option类。据我所知,它缺少map方法,这使得整个事情在我看来是个坏笑话。但是,由于最重要的工具(匿名函数)是由一个常见的嫌疑人(Google,Apache ......)提供的适当的Option实现。

答案 2 :(得分:1)

按照目前的情况,你可能会写一个像

这样的方法
public void clauseIfNotNull(Builder builder, String format, Object o) {
  if (o != null) {
    builder.clause(String.format(format, o));
  }
}

然后看起来像clauseIfNotNull(builder, "username = \"%s\"", ac.getUsername());

除此之外,Java 7的功能并不多。

答案 3 :(得分:1)

在Builder

上创建一个最小的适配器对象
class NotNullClauseAdapter
{
   private final Builder builder;
   public NotNullClauseAdapter(Builder builder) {
      this.builder = builder;
   }
   public void clause(String format, Object o) {
      if (o != null) {
        builder.clause(String.format(format, o));
      }
   }
}

在您的代码中使用它:

for (AccountConstraint ac : c) {
  Builder builder = new QueryBuilder.Builder("SELECT * FROM accounts");
  NotNullClauseAdapter adapter = new NotNullClauseAdapter(builder);
  if (ac.getAccountId() != null) {
      builder.clause("accountId >= " + ac.getAccountId().getMin() + " AND accountId <= " + ac.getAccountId().getMax());
  }
  adapter.clause("username = \"%s\"", ac.getUserName());
  adapter.clause("password = \"%s\"", ac.getPassword));
  adapter.clause("email = \"%s\"", ac.getEmail());
  PreparedStatement ps = connection.prepareStatement(builder.build().getQuery());
  ResultSet rs = ps.executeQuery();
  while (rs.next()) {
    beans.add(new AccountBean(rs));
  }
}

您可以通过向适配器添加其他子句方法来扩展,以处理特定对象(如范围),以便转换像accountId这样的内容,例如。

public void clauseMinMax(String format, Range r) {
  if (r != null) {
     builder.clause(String.format(format, r.getMin(), r.getMax()));
  }
}

然后,accountId行变为(如果getAccountId()返回Range对象):

adapter.clauseMinMax("accountId >= %d AND accountId <= %d", ac.getAccountId());

答案 4 :(得分:0)

使用JSR305并使用适当的@Nonnull注释,您不必进行空检查,注释会为您完成。

使用JSR305中的@Nonnull@CheckReturnValue注释有助于表达对null和返回值检查的需求。开发人员描述实现的预期行为以供以后使用和静态代码分析是一种很好的做法。