#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);
Для модульных тестов вы можете использовать функции утверждения Junit ( org.junit.Assert
) или Jupiter ( org.junit.jupiter.api.Assertions
).
Комментарии:
1. Одним из недостатков обычной функции, такой как Junit или Spring, является то, что они всегда создают строку сообщения, даже если утверждение проходит; в высокопроизводительных системах дополнительные временные объекты могут оказать значительное влияние. (Вот почему функция Kotlin, конечно, принимает лямбда-выражение.)
2. @gidds Существует перегрузка, которая принимает a
Supplier
в качестве второго параметра для SpringAssert
, поэтому этот ответ можно было бы немного улучшить, чтобы он соответствовал исходному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.