Есть ли встроенная Java, эквивалентная функции require от Kotlin?

#java #kotlin

#java #котлин

Вопрос:

У Kotlin есть функция require, которую можно использовать следующим образом (скопировано из справочной документации):

 fun getIndices(count: Int): List<Int> {
    require(count >= 0) { "Count must be non-negative, was $count" }
    // ...
    return List(count) { it   1 }
}

// getIndices(-1) // will fail with IllegalArgumentException

println(getIndices(3)) // [1, 2, 3]
 

Функция по существу выдает исключение IllegalArgumentException, если значение равно false.
Очевидно, что это может быть очень легко реализовано на Java, но мне было интересно, есть ли что-то уже в библиотеках JDK или apache (или любых других вездесущих библиотеках), предлагающее функцию, которая это делает?

Комментарии:

1. В Java нет таких встроенных модулей, но это выглядит точно так же, как Preconditions и в Google Guava. Подробнее см. По адресу: github.com/google/guava/wiki/PreconditionsExplained

Ответ №1:

Вы можете использовать assert функцию, эквивалентную методу Kotlin require .

 assert count >= 0 : "Count must be non-negative, was "   count;
 

Программирование с утверждениями

JDK по умолчанию отключает операции утверждения. Если вы хотите включить операции assert, вы должны определить разрешенные местоположения пакетов или классов с параметрами виртуальной машины, такими как -ea:com.example.demo...

Включение и отключение утверждений

Я предпочитаю org.springframework.util.Assert класс Spring Framework, потому что существует множество методов проверки параметров.

Более простой способ:

 Assert.isTrue(count >= 0, "Count must be non-negative, was "   count);
 

Ленивый способ (для повышения производительности и того же потока, что require и функция kotlin):

 Assert.isTrue(count >= 0, () -> "Count must be non-negative, was "   count);
 

Операторы Spring Assert

Для модульных тестов вы можете использовать функции утверждения Junit ( org.junit.Assert ) или Jupiter ( org.junit.jupiter.api.Assertions ).

Комментарии:

1. Одним из недостатков обычной функции, такой как Junit или Spring, является то, что они всегда создают строку сообщения, даже если утверждение проходит; в высокопроизводительных системах дополнительные временные объекты могут оказать значительное влияние. (Вот почему функция Kotlin, конечно, принимает лямбда-выражение.)

2. @gidds Существует перегрузка, которая принимает a Supplier в качестве второго параметра для Spring Assert , поэтому этот ответ можно было бы немного улучшить, чтобы он соответствовал исходному require поведению.

Ответ №2:

Существует метод requireNonNull() , java.util.Objects который принимает ссылку на объект и проверяет ее на наличие null .

Смотрите Javadoc здесь!

Но, очевидно, это менее гибко, чем версия Kotlin …

В принципе, вы можете написать свою собственную версию require() , подобную этой:

 public static final void require( final boolean predicate, final Supplier<String> messageSupplier )
{
  if( !predicate ) throw new IllegalArgumentException( messageSupplier.get() );
}
 

(Обработка ошибок опущена …)

Вы можете использовать ее следующим образом:

require( count > 0, () -> String.format( "Count must be non-negative, was %d", count );
…
 

Но для этого требуется, чтобы это count было эффективно final (или постоянно), иначе оно не будет компилироваться.

Это можно обойти при создании require() подобным образом:

 public static final void require( final boolean predicate, final Object messageArgument, final Function<Object,String> messageSupplier )
{
  if( !predicate ) throw new IllegalArgumentException( messageSupplier.apply( Objects.toString( messageArgument ) );
}
 

Вы можете использовать ее следующим образом:

require( count > 0, count, arg -> String.format( "Count must be non-negative, was %s", arg );
…
 

Но в случае примитивного типа вам придется заплатить цену за бокс при вызове require() .

Этого, в свою очередь, можно избежать, имея целое семейство require() методов, по одному для каждого примитивного типа …

Если основная цель состоит в том, чтобы избежать временных объектов, которые не используются при выполнении условия predicate , я бы предположил, что вам следует использовать не встроенные замыкания ( () -> … ), а ссылки на методы ( this::getMessage ) в качестве аргументов Supplier / Function … но я также думаю, что мы здесь уже на пути преждевременной оптимизации.

Ответ №3:

 public class TestAsserts {
    public static void main(String[] argv) {

        int count = -2;

        testCount(count);
    }

    public static void testCount(int count) {
        assert(count >= 0): "Count must be non-negative, was "   count;
    }
}
 

будет работать так же. Вы сами решаете, включены утверждения или нет во время выполнения. Чтобы запустить это, вам нужно будет передать -ea в качестве параметра командной строки Java.

Обратите внимание, что исключение тоже немного отличается:

 Exception in thread "main" java.lang.AssertionError: Count must be non-negative, was -2
    at TestAsserts.testCount(TestAsserts.java:12)
    at TestAsserts.main(TestAsserts.java:8)
 

Комментарии:

1. Несмотря на то, что она выглядит как хорошая замена, она необязательна в JVM ( -ea хотя вы упомянули: как правило, проверка утверждений включена во время разработки и тестирования программы и отключена для развертывания, для повышения производительности. из JLS), семантика утверждения отличается и указывает на то, что никогда не должно происходить (например, переключение всех значений enum с регистром по умолчанию без перечисления); он не выдает IllegalArgumentException , а выдает ошибку утверждения.

2. Чтобы добавить к комментарию @fluffy: ошибки не должны (никогда?) Перехватываться и обрабатываться; очевидно, это отличается для исключений (во время выполнения). Так же и с этой точки зрения, assert это плохая альтернатива.

3.@tquadrat Да. … Опять же, использование утверждений для проверки аргументов в общедоступных методах не является незаконным, но, как правило, это неуместно. Предполагается, что AssertionError никогда не будет перехвачен, но это возможно, поэтому правила для операторов try должны обрабатывать утверждения, появляющиеся в блоке try, аналогично текущей обработке throw statements.docs.oracle.com/javase/specs/jls/se7/html/jls-14.html#jls-14.10


Ответ №4:

Начиная с Java 1.4, в JDK есть ключевое слово assert, которое можно использовать для выполнения чего-то очень похожего. В этом случае генерируется ошибка assertrionerror.