В Java 19 я пытаюсь использовать сопоставление шаблонов instanceof
внутри оператора assert
.
Я ожидаю, что соответствующий тип будет доступен после оператора, но компилятор Java не распознает новую переменную.
Мы создаем переменную Object obj
и не можем знать, содержит ли она Integer
или String
.
Мы передаем переменную двум методам тестирования.
public class AssertPatternMatching {
public static void main(String[] args) {
Object obj = args.length == 0 ? Integer.valueOf(42) : "Hello";
afterAssert(obj);
insideMessage(obj);
}
В методе afterAssert()
мы утверждаем, что obj
является String
, и привязываем строку к новой переменной str
посредством instanceof
сопоставления с образцом.
Я ожидаю, что str
будет известен и может использоваться в следующей строке.
Однако компилятор не знает символ str
.
private static void afterAssert(Object obj) {
assert obj instanceof String str;
str.length(); // javac: cannot find symbol variable str
}
В методе insideMessage()
мы используем запутанный оператор assert
, чтобы проверить, что obj
не является String
. Если это так, утверждение не выполняется, и мы можем предоставить сообщение.
Поскольку проверка instanceof
отменяется, переменная str
, соответствующая шаблону, должна быть доступна для сообщения об ошибке.
Однако компилятор снова не знает символа str
.
private static void insideMessage(Object obj) {
assert !(obj instanceof String str) : "Is a string: " + str.length();
// line above: javac: cannot find symbol variable str
obj.hashCode();
}
Это сработает, если мы заменим операторы assert
на if
:
private static void afterAssertIf(Object obj) {
if (obj instanceof String str) {
str.length();
} else {
throw new AssertionError();
}
}
private static void insideMessageIf(Object obj) {
if (!(obj instanceof String str)) {
obj.hashCode();
} else {
throw new AssertionError("Is a string: "+ str.length());
}
}
Пример InsideMessage() действительно крайний случай, поэтому я понимаю, что он не поддерживается. Однако я ожидал, что afterAssert() сработает. Это преднамеренный выбор дизайна или ошибка? Если преднамеренно, то в чем причина?
Полный код на https://gist.github.com/enikao/57bb1b10ce3126494ec4baa2bc7db2df
Операторы assert
выполняются только тогда, когда утверждения включены с помощью -ea
. Без -ea
код не выполняется, поэтому переменная никогда не присваивается/не существует (по умолчанию).
private static void afterAssert(Object obj) {
assert obj instanceof String;
((String) str).length();
}
Поскольку компилятор не может знать, будет ли выполнен оператор assert
, он не может полагаться на то, что переменные, объявленные в операторе, будут доступны вне оператора (поскольку оператор мог никогда не выполняться).
Да, @werkderk, -ea
— это флаг времени выполнения. Так что нет, поведение компилятора не может предвидеть предоставленное значение. Но, с другой стороны, скомпилированный код должен работать, когда -ea
не предоставляется, и в этом случае instanceof
не выполняется. Отсюда следует, что область действия идентификатора, совпадающего с шаблоном, не может выходить за пределы оператора assert
.
Спасибо @JohnBollinger, имеет смысл. Так что теоретически мой замысловатый пример insideMessage() должен работать, так как выражение сообщения оценивается только в том случае, если утверждение терпит неудачу. Но никто не стал бы писать такой ассерт (надеюсь!)
@werkderk прежде всего, утверждения не должны применяться к параметрам. Утверждения предназначены для проверки инвариантов, которые должен поддерживать код, а входящие параметры противоположны этому. Стандартная идиома — это явная проверка, отклоняющая недопустимые аргументы с помощью IllegalArgumentException
. Сопоставление с образцом instanceof гладко работает с идиоматическим подходом. Было бы логично, если бы ваш пример в insideMessage
работал, но, как обсуждалось в в этом вопросе и ответе, только конструкции, названные JLS явно «должны работать». И assert среди них нет.
-ea
— это флаг времени выполнения, верно? Это не должно влиять на компилятор?