Различное поведение одного и того же оператора при выполнении в JShell

Я работал над проблемой хранения ссылок на два класса друг в друге.

Например:

class A {
B b;
A(B b){
this.b = b;}
}

class B {
A a;
B(A a){
this.a = a;}
}

public static void main(String...s){
A a = new A(new B(null));
a.b.a = a;
}

Теперь, если вместо инициализации выше, если я использую инструкцию ниже:

A a = new A(new B(a));

Я получил ошибку ниже, которая совершенно очевидна:

Main.java:19: error: variable a might not have been initialised
        A a = new A(new B(a));

Но если я попробую то же самое в JShell, все будет работать нормально (просто чтобы быть уверенным, что variable a никогда не инициализировалось, я проверил variable a непосредственно перед выполнением оператора, который подтверждает, что он не был инициализирован раньше:

введите здесь описание изображения

Может быть, я что-то здесь упускаю, но может кто-нибудь, пожалуйста, помогите мне понять, почему в JAVA выполняются два разных поведения одного и того же оператора.

Простой способ понять эту проблему заключается в том, что приведенный ниже оператор разрешен в Jshell, но не в обычной программе:

var somevar = somevar;

person Aman Chhabra    schedule 31.03.2018    source источник
comment
Нет, это не так, я проверил незадолго до этого и получил | Ошибка: | не могу найти символ | символ: переменная а | а | ^   -  person Aman Chhabra    schedule 31.03.2018
comment
@DawoodibnKareem также обновил скриншот.   -  person Aman Chhabra    schedule 31.03.2018
comment
Если он действительно делает то, что вы говорите, мне кажется, что это ошибка в jshell. На вашем месте я бы сообщил об этом Oracle.   -  person Dawood ibn Kareem    schedule 31.03.2018
comment
Спасибо @DawoodibnKareem   -  person Aman Chhabra    schedule 31.03.2018
comment
это циклическая зависимость. это?   -  person nidhin    schedule 31.03.2018
comment
Да, верно, но как общий, он отлично работает в обоих случаях, но с разными утверждениями @nidhin   -  person Aman Chhabra    schedule 31.03.2018
comment
@DawoodibnKareem Не ошибка. См. мой ответ.   -  person Andreas    schedule 31.03.2018
comment
Более простым примером мог бы быть Integer x = x, что вы ожидаете от компилятора? Jshell в значительной степени работает так, как объяснил Андреас.   -  person Naman    schedule 31.03.2018
comment
@nullpointer Я уже поделился тем, что компилятор обычно делает в таких сценариях. Определенно объяснение Андреаса имеет смысл   -  person Aman Chhabra    schedule 31.03.2018
comment
Это принято как ошибка сейчас. См. stackoverflow.com/a/49888971/1262248   -  person Aman Chhabra    schedule 18.04.2018


Ответы (2)


Оператор A a = new A(new B(a)); не является объявлением локальной переменной.

Но сначала проблему, которую вы описываете, можно упростить до:

jshell> int a = a;
a ==> 0

Теперь, как это работало?

Ну, как говорится в Snippets из JEP 222:

В JShell переменная является местом хранения и имеет связанный тип. Переменная создается явным образом с помощью фрагмента FieldDeclaration:

int a = 42;

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

Таким образом, они ведут себя немного как поля и немного как локальные переменные.

Подобно полю, но в отличие от локальной переменной, объявление без инициализатора присвоит значение по умолчанию, например.

jshell> int b;
b ==> 0

Но вернемся к int a = a;. В разделе State JEP 222 говорится:

Состояние JShell хранится в экземпляре JShell. Фрагмент оценивается в JShell с помощью метода eval(...), создавая ошибку, объявляя код или выполняя оператор или выражение. В случае переменной с инициализатором происходит как объявление, так и выполнение.

Итак, объявление переменной и выполнение инициализатора — это два отдельных действия.

Это означает, что при выполнении инициализатора переменная уже была объявлена ​​и ей уже было присвоено значение по умолчанию.

По сути, int a = a; оценивается как:

jshell> int a;
a ==> 0

jshell> a = a;
a ==> 0

Вот как работает jshell REPL. Это не ошибка.

person Andreas    schedule 31.03.2018
comment
Спасибо @Андреас. Это имеет смысл. - person Aman Chhabra; 31.03.2018
comment
Спасибо @Андреас. Это отвечает на вопрос. Процитированная вами спецификация на самом деле была документацией, которую я искал, прежде чем опубликовать свой комментарий. Тем не менее, я по-прежнему не согласен с классификацией этого как ошибки. Oracle описала цель jshell как ...изучение языка программирования Java и прототипирование кода Java.... Тот факт, что в некоторых случаях jshell ведет себя иначе, чем Java, означает, что jshell не подходит для этой цели. Это ошибка дизайна, независимо от того, описывает ли Oracle ошибочное поведение в спецификации. - person Dawood ibn Kareem; 31.03.2018
comment
@DawoodibnKareem По своей природе REPL нарушает синтаксис Java (например, операторы вне методов), поэтому, хотя намерение состоит в том, чтобы разрешить прототипирование, оно не может быть на 100% верным Ява. Если это то, что вы ожидаете/хотите, не используйте REPL. Это один из примеров задокументированного различия. Разница заключается в дизайне. Поскольку 100% Java в REPL невозможна, вы не можете утверждать, что конструктивные различия, которые делают REPL действительно работоспособным, являются ошибкой дизайна. - person Andreas; 31.03.2018
comment
Можно утверждать, что JShell просто следует Spec: Хотя присваивания являются правоассоциативными (т. е. a=b=c означает a=(b=c)), порядок обработки операторов в случае присваивания переменных описывает: [...]требуются три шага: во-первых, левый Операнд -hand оценивается для создания переменной.[...] - person SME_Dev; 27.09.2019

Хотя я полностью согласен с ответом @Andreas выше, что это ожидается с тем, как инициализация в настоящее время работает в JShell, я также придерживаюсь того же мнения, что и @Dawood, что JShell должен работать как можно ближе к обычному компилятору, иначе иногда это может быть неопределенно и, следовательно, небезопасно для использования.

Для этого конкретного сценария я поднял проблему с Oracle, и теперь она признана официальной ошибкой. Вы можете отслеживать больше об этой ошибке здесь:

https://bugs.java.com/bugdatabase/view_bug.do?bug_id=JDK-8200567

Надеюсь, что это скоро будет решено в следующих выпусках JShell.

person Aman Chhabra    schedule 17.04.2018