为何Java中final修饰的未初始化枚举变量经switch赋值无报错?
我在清理代码时尝试为尽可能多的位置添加final关键字,不慎给一个未初始化的enum类型变量也加上了final修饰符。该变量原本需要通过switch语句确定取值,理论上此时将其设为final并不合理,但意外发现代码运行完全正常,未产生任何报错。根据我的理解,Java中未初始化的变量会持有默认值(此处应为null),而final变量无法被重新赋值,因此我疑惑:为何这段代码既没有报错也没有产生副作用?
注:NeighborPosition是一个枚举类型,代码如下:
final NeighborPosition edgePosition; switch(cornerPosition) { case ONE: edgePosition = NeighborPosition.THREE; break; case TWO: edgePosition = NeighborPosition.FOUR; break; case THREE: edgePosition = NeighborPosition.FIVE; break; case FOUR: edgePosition = NeighborPosition.SIX; break; case FIVE: edgePosition = NeighborPosition.ONE; break; case SIX: edgePosition = NeighborPosition.TWO; break; case ZERO: default: { throw new IllegalStateException( "Should never reach this corner, current corner: " + cornerPosition); } }
解答
这是因为Java对final局部变量的赋值规则并非“声明时必须初始化”,而是在变量第一次被使用前必须被明确赋值一次,且仅能赋值一次,你的代码完全符合这个规则:
编译器的明确赋值检查:
Java编译器会分析代码执行路径,它能识别出:所有正常的case分支(ONE到SIX)都会给edgePosition赋一次值,且每个分支只会赋值一次;而走到ZERO或default分支时,程序会直接抛出异常终止,不会进入到需要使用edgePosition的后续代码。final局部变量没有默认值:
你提到的“未初始化变量持有默认值”是针对非final的成员变量或局部变量,但final局部变量不会被赋予默认值——编译器强制要求开发者必须在变量被使用前手动完成赋值,否则会直接触发编译错误。你的代码中所有可能走到的、需要使用变量的路径都完成了赋值,因此编译器不会报错。无副作用的原因:
每个合法分支只赋值一次,完全满足final变量“只能赋值一次”的要求;而异常分支直接终止程序,不会出现变量未赋值就被使用的情况,自然也不会有副作用。
内容的提问来源于stack exchange,提问作者Quan Bui

