2013-06-28 3 views
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, потому что иначе код потерпит неудачу.

Возможно ли преобразовать операторы if (object != null) в однострочные, чтобы только выполнить, если нет NullPointerException? Когда есть исключение, эту строку следует просто игнорировать.

Я не говорю об общей функции языка здесь, я говорю об одной функции, которая была бы включена, когда вы решительно решите это сделать.

Например: NullCheck(builder.clause("username = \"" + ac.getUsername() + "\"")); был бы фрагментом предложенного кода.

Возможно ли это на Java?

Также, если это невозможно, возможно ли в Java 8 использовать методы (пустоты) непосредственно в методах?

Итак, код, подобный этому, может действительно работать?

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

Я знаю, что я мог бы поставить метод внутри его собственный класс, который расширяет интерфейс, который имеет метод execute(), а затем передать этот класс вокруг, но это поражение цели, чтобы избавиться от пустых чеков или ничего, что бы быть еще более сложным.

С уважением.

ВНИМАНИЕ: способ, которым я использовал PreparedStatement, в данном случае подвержен SQL-инъекции. Не используйте этот код повторно.

+0

Я также задавался об этом. Я не думаю, что есть ... если вы посмотрите на большие пакеты с открытым исходным кодом, такие как Oozie, у них есть проверка или функция нулевого класса. – jmpyle771

+1

Надеюсь, что этот метод не используется для ввода пользователем (что, вероятно, есть), потому что это гигантская инъекция SQL-инъекций, ожидающая своего появления. вы должны правильно использовать PreparedStatement (используя параметры, а не жестко закодированные значения). – jtahlborn

+0

@jtahlborn Спасибо за головы. Я действительно полностью забыл использовать его правильно, однако я использовал его правильно при вставке/удалении, так что это просто вопрос о том, чтобы забыть его с грустью. Еще раз спасибо. – skiwi

ответ

2

Да и Нет.

Есть два подхода к решению этой проблемы: нулевой

Специальные операторы как навигации оператора безопасной в Groovy. Если x.y throws a NullPointerExceptionx?.y возвращает только null. Поскольку Java не позволяет создавать новые операторы, вы не можете сделать это на Java. Операторы вроде этого, где рассматриваются для JDK8, но где они упали. Если вы хотите иметь что-то вроде этого, переключитесь на Groovy или на один из многих других языков, имеющих эту функцию.

Специальный класс Многие языки имеют специальный интерфейс для представления значения, которое может быть null. В Scala он называется Option. Вариант имеет две реализации: None + Some. Ни один не заменяет null. Всякий раз, когда вы хотите что-то сделать со значением, вы не используете его напрямую, но вы вызываете map на Option с функцией в качестве аргумента. Если это на самом деле нет, ничего не происходит, вы просто возвращаетесь Нет. Если это некоторая функция, функция запускается по значению, и вы получаете опцию с результатом. Таким образом, вы можете работать с настройками все время, не беспокоясь о null s.

На самом деле это сейчас специально, поэтому вы можете создать такой класс самостоятельно с помощью Java. Проблема только в том, что Java не имеет функций, поэтому вам нужно использовать анонимные классы. Это делает все это действительно громоздким и только теоретическим вариантом.

JDK8 имеет класс Option. Насколько мне известно, отсутствует метод map, который делает все это плохой шуткой, на мой взгляд. Но поскольку наиболее важным инструментом (анонимными функциями) будет надлежащая реализация Option, предоставленная одним из обычных подозреваемых (Google, Apache ...)

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.

+0

Что относительно Java 8? Об этом уже известно что-то конкретное? – skiwi

+0

Несомненно, вы могли бы написать метод, чтобы сказать «просто отмените, если это выдает« NullPointerException », и даже было бы удобно читать с помощью Java 8, хотя это было бы не так эффективно. –

+2

@skiwi Я не на самом деле подумайте, что вы сможете написать что-то лучше на Java 8. Я думаю, что этот ответ выглядит достаточно приличным, я бы сделал то же самое. – Malcolm

3

Вы можете избежать этих проверок с помощью , считая, что эти методы не возвращают нулевые значения.

Как вы можете это принять? Имея спецификацию для AccountConstraint, скажите так. Затем код в AccountConstraint отвечает за то, что значения не равны нулю, а не ваш метод search, отвечающий за обработку нулевых значений. Возможно, вам придется изменить дизайн AccountConstraint.

И что произойдет, если ваше предположение неверно? То есть, если AccountConstraint неисправен. Будет выброшено исключение, которого вы не ожидали. Но это то, что может произойти, когда у вас есть ошибка: возникает неожиданное исключение. Отладка кода будет простой, потому что stacktrace покажет вам, какой метод AccountConstraint возвращает недопустимое значение null.

+0

Это предположение нельзя сделать как значения «null» в 'AccountConstraint', которые указывают, что нет Ограничение на эту конкретную область. Также дружественное примечание, пожалуйста, пересмотрите свой ответ, поскольку вся нулевая вещь связана с 'AccountConstraint', а не с' AccountBean'. – skiwi

+0

@skiwi теперь исправлены. – Raedwald

0

Используйте JSR305 и используйте соответствующие аннотации @Nonnull, и вы не должны делать нулевые проверки, аннотации делают их для вас.

Использование @Nonnull и @CheckReturnValue аннотаций от JSR305 помогает выразить потребности в проверках нулевого и возвратного значений. Хорошей практикой является то, что разработчик описывает ожидаемое поведение реализации для последующего использования и анализ статического кода.

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());