我正在通过Oracle的官方docs来了解Java 17中的Pattern Variablescope。在下面的示例中,方法
testScope1
按照文档中的说明工作,但是方法testScope2
给出了编译错误。我无法弄清楚为什么 void
方法的返回类型会导致问题?
interface Vehicle{}
class Car implements Vehicle{}
class Pattern{
public int testScope1(Vehicle v){
if(!(v instanceof Car c)){
return 1;
}
System.out.println(c.toString()); //WORKS FINE
return 2;
}
public void testScope2(Vehicle v){
if(!(v instanceof Car c)){
}
System.out.println(c.toString()); //COMPILE TIME ERROR: Cannot resolve symbol c
}
}
模式变量(在模式中声明的绑定变量)使用流敏感范围。与普通局部变量在连续区域的范围内不同,模式变量在范围内 它们将由其声明模式明确分配。
如果您有
if
声明:
if (x instanceof Foo(var v)) {
A;
}
else {
B;
}
那么
v
就在 A
的范围内,但不在 B
的范围内,因为我们不能保证在达到 v
的情况下一定会为 B
分配一个值。如果我们使用明显的重构来反转我们的测试:
if (!(x instanceof Foo(var v))) {
B;
}
else {
A;
}
同样如此;
v
位于 A
范围内,但不在 B
范围内。这些规则与局部变量的明确赋值规则完全相同——“如果我达到这一点,这个值是否保证已被赋值。”
其他条件构造,例如短路
&&
和 ||
,也参与此范围界定。例如,以下内容是有效的:
if (x instanceof Foo(var v) && v != null) {
A;
}
但以下不是:
if (x instanceof Foo(var v) || v != null) {
A;
}
因为在后者中,当我们到达
v
子句时,不能保证为 v != null
赋值。
这些规则甚至包含非本地控制流,例如异常。例如,如果我们有:
if (!(x instanceof Foo(var v)) {
System.out.println("Not a Foo");
}
B(v);
这将是一个错误,因为当我们到达
v
时,不能保证为B(v)
分配了一个值,但是如果if
块突然完成:
if (!(x instanceof Foo(var v)) {
throw new NotFooException();
}
B(v);
那么
v
就在 B(v)
的范围内,因为我们保证,如果到达该点,v
已被分配了一个值。
这可能看起来很复杂,但实际上非常简单:鉴于您对
if
、throw
等结构的流程控制的了解,模式变量是否保证在给定点被赋值?如果是这样,那么它就在那时的范围内。
考虑一下如果
v
是 不是 Car
实例会发生什么:
testScope1
中,return 1;
语句导致方法退出。该方法的后续语句不会被执行。一切都好。testScope2
中没有return
,因此控制流程达到c.toString()
。但 v
不是 Car
,所以……c
是什么?!它不可能存在,因为它必须是 Car
类型,但这是反事实的。这就是为什么您收到错误“无法解析符号 c
”。经过更多思考,我有了一个解释:只有当该代码段的instanceof明显为真时,才能访问该变量。
在 testScope1 中,您无法在 if 语句中访问
c
。
但由于 if 语句之后的代码仅在 instanceof 为 true 时执行,因此 c
是可以访问的。
在 testScope1 中,您无法在 if 语句中访问
c
。
但是,由于无论instanceof为真还是假,if语句之后的代码都会被执行,c
是不可访问的。
毕竟我们可能会遇到错误的情况,编译器会采取相应的行动。
放置一条 else 语句。仅当
instanceof
运算符产生 true
时才会创建模式变量。否则作用域中没有变量,因此会出现编译器错误。
public void testScope2(Vehicle v){
if(!(v instanceof Car c)){
} else {
System.out.println(c.toString());
}
}
编辑:
testScope1
方法不需要else
语句,因为return
语句已经存在。 return
语句确保如果 c.toString()
不是 v
的实例,则控制永远不会到达 Car
。
Pattern 变量只是 java 14 中添加的语法黑巧克力。生成字节码时,它与简单的
instanceof
和 if-else
语句没有什么区别。例如,我使用 java 17 编译了您的 Pattern
类,并使用 java 8 反编译。这是 testScope1
方法的反编译版本。 Java 17 编译器本身放置了一个 else
:
public int testScope1(Vehicle var1) {
if (var1 instanceof Car) {
Car var2 = (Car)var1;
System.out.println(var2.toString());
return 2;
} else {
return 1;
}
}
所以,你可以看到,java编译器修改了你的
if
语句,这样只有当Car
是v
instanceof
时才会创建Car
变量。简单的概念 instanceof
、TypeCasting
、variable scopes
和 if-else
已被使用,这些概念已经存在很久了。
我还是不明白为什么。
public void testScope2(Vehicle v){
if(!(v instanceof Car c)){
System.out.println();
return; //with this, works. But I still don't understand why.
}
System.out.println(c.toString()); //no more error
}