1

我想知道是否有可能在 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);
    }
}

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

是否可以将语句转换为仅在没有 NullPointerException时才if (object != null)执行的单行语句?当出现异常时,应该忽略该行。

我在这里不是在谈论通用语言功能,我说的是只有在您明确决定这样做时才会上交的功能。

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

在 Java 中可能有这样的事情吗?

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

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

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

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

问候。

警告:我在这里使用 PreparedStatement 的方式很容易发生 SQL 注入。不要重复使用此代码。

4

5 回答 5

3

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

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

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

于 2013-06-28T22:17:45.737 回答
2

是和否。

有两种方法可以解决空问题:

特殊运算符,例如 Groovy 中的安全导航运算符。如果x.y抛出一个NullPointerException x?.y返回只是null。由于 Java 不允许创建新的运算符,因此您不能在 Java 中执行此操作。像这样的运算符在 JDK8 中被考虑过,但在哪里被丢弃。如果您想要这样的东西,请切换到 Groovy 或具有此功能的许多其他语言之一。

特殊类很多语言都有一个特殊的接口来表示一个可能是null. 在 Scala 中,它被称为 Option。Option 有两种实现方式:None+ Some。无替代null。每当你想对这个值做某事时,你都不会直接使用它,而是map用一个函数作为参数调用 Option。如果它实际上是一个 None,则什么也不会发生,您只需返回 None。如果它是 Some,则函数会在该值上执行,并且您会得到一个带有结果的 Option。这样你就可以一直使用 Options,而不用担心nulls.

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

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

于 2013-06-29T12:04:09.817 回答
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());
于 2013-06-29T12:20:04.807 回答
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 无能为力。

于 2013-06-28T21:19:04.393 回答
0

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

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

于 2013-06-29T12:17:59.700 回答