Java 在JShell上执行同一语句时的不同行为

Java 在JShell上执行同一语句时的不同行为,java,java-9,jshell,java-10,Java,Java 9,Jshell,Java 10,我正在解决一个问题,即在彼此之间存储两个类的引用 例如: 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

我正在解决一个问题,即在彼此之间存储两个类的引用

例如:

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上尝试相同的方法,它工作得很好(只是为了额外确保
变量a
从未初始化,我在执行语句之前检查了
变量a
,该语句确认它之前未初始化:

也许我在这里遗漏了一些东西,但是有人能帮助我理解为什么在JAVA中执行同一语句时会有两种不同的行为

理解此问题的一个简单方法是,在
Jshell
中允许以下语句,但在普通程序中不允许:

var somevar = somevar;

语句
aa=newa(newb(A));
不是局部变量的声明

但首先,您描述的问题可以简化为:

jshell> int a = a;
a ==> 0
那是怎么回事

那么,截至JEP222,它说:

在JShell中,“变量”是一个存储位置,具有关联的类型。变量是使用FieldDeclaration片段显式创建的:

或通过表达式隐式地(见下文)。变量具有少量字段语义/语法(例如,允许使用
volatile
修饰符)。但是,变量没有包含它们的用户可见类,通常会像局部变量一样查看和使用

所以,它们的行为有点像字段,也有点像局部变量

与字段类似,但与局部变量不同,没有初始值设定项的声明将指定默认值,例如

jshell> int b;
b ==> 0
但是,回到JEP222的
inta=a;
。部分说:

JShell状态保存在JShell实例中。使用
eval(…)
方法在JShell中计算代码段,生成错误、声明代码或执行语句或表达式。对于带有初始值设定项的变量,声明和执行都会发生

因此,变量的声明和初始值设定项的执行是两个独立的操作

这意味着在执行初始值设定项时,变量已经被声明,并且已经被分配了默认值

实际上,
int a=a;
的计算结果如下:

jshell> int a;
a ==> 0

jshell> a = a;
a ==> 0

这就是jshell REPL的工作原理。它不是一个bug。

尽管我完全同意上面@Andreas的回答,即这是jshell中当前初始化工作的预期方式,但我也和@Dawood一样认为jshell应该尽可能接近普通编译器,否则有时可能不确定因此使用不安全

对于这个特定场景,我向Oracle提出了一个问题,现在它已被接受为一个官方错误。您可以在此处跟踪有关此错误的更多信息:


希望这一问题能在即将发布的JShell中很快得到解决。

不,事实并非如此,我在这之前检查了a,发现|错误:|找不到符号|符号:变量a | a | ^@DawoodibnKareem也更新了屏幕截图。如果它真的在做你说的话,我觉得它就像JShell中的一个bug。如果我是你,我就是你d向Oracle报告。@DawoodibnKareem不是一个bug。请参阅。一个更简单的例子可能是
整数x=x
,您希望编译器做什么?Jshell非常感谢@Andreas。这很有道理。感谢@Andreas。它回答了这个问题。您引用的规范实际上就是我正在寻找的文档在我发表我的评论之前。然而,我仍然不同意将其归类为“不是bug”。Oracle将jshell的目的描述为“…学习Java编程语言并原型化Java代码…”。在某些情况下,jshell的行为与Java不同,这意味着jshell无法达到目的。这是一个设计缺陷,无论Oracle是否在规范中描述了错误行为。@DawoodibnKareem从本质上讲,REPL违反了Java语法(例如,方法外的语句),因此,尽管其目的是允许原型化,但它不可能100%适用于Java。如果这是您期望/想要的,请不要使用REPL。这是一个有文档记录的差异的示例。这种差异是由设计造成的。由于100%Java在REPL中是不可能的,因此您不能声称使REPL实际可行的设计差异是一种错误“设计错误”。有人可能会说JShell只是遵循以下步骤:虽然赋值是右关联的(即
a=b=c
意味着
a=(b=c)
),但变量赋值的语句处理顺序描述:[……]需要三个步骤:首先,对左侧操作数求值以生成变量。[……]
jshell> int a;
a ==> 0

jshell> a = a;
a ==> 0