Java 模式变量作用域

问题描述 投票:0回答:5

我正在通过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
    }
}
java instanceof java-17
5个回答
12
投票

模式变量(在模式中声明的绑定变量)使用流敏感范围。与普通局部变量在连续区域的范围内不同,模式变量在范围内 它们将由其声明模式明确分配

如果您有

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
等结构的流程控制的了解,模式变量是否保证在给定点被赋值?如果是这样,那么它就在那时的范围内。


7
投票

考虑一下如果

v
不是
Car
实例会发生什么:

  • testScope1
    中,
    return 1;
    语句导致方法退出。该方法的后续语句不会被执行。一切都好。
  • testScope2
    中没有
    return
    ,因此控制流程达到
    c.toString()
    。但
    v
    不是
    Car
    ,所以……
    c
    是什么?!它不可能存在,因为它必须是
    Car
    类型,但这是反事实的。这就是为什么您收到错误“无法解析符号
    c
    ”。

1
投票

经过更多思考,我有了一个解释:只有当该代码段的instanceof明显为真时,才能访问该变量。

在 testScope1 中,您无法在 if 语句中访问

c
。 但由于 if 语句之后的代码仅在 instanceof 为 true 时执行,因此
c
是可以访问的。

在 testScope1 中,您无法在 if 语句中访问

c
。 但是,由于无论instanceof为真还是假,if语句之后的代码都会被执行,
c
是不可访问的。 毕竟我们可能会遇到错误的情况,编译器会采取相应的行动。


1
投票

放置一条 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
已被使用,这些概念已经存在很久了。


0
投票

我还是不明白为什么。

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
}
© www.soinside.com 2019 - 2024. All rights reserved.