Java停止了内部类中非最终变量的错误(java 8)[重复]

Posted

技术标签:

【中文标题】Java停止了内部类中非最终变量的错误(java 8)[重复]【英文标题】:Java stopped erroring on non-final variables in inner classes (java 8) [duplicate] 【发布时间】:2015-04-09 02:16:04 【问题描述】:

Java 7 在以下代码中说“不能引用在封闭范围内定义的非最终局部变量消息”:

public class Runner    
    public static void main(String[] args) 

        String message = "Hello world";

        new Runnable() 
            @Override
            public void run() 
                System.out.println(message);
            
        .run();
    

Java 8 没有。

怀疑这是为了给 Java 添加函数式编程特性。

它是否类似地处理代码?

【问题讨论】:

【参考方案1】:

Java 8 隐含地将 message 设为 final,因为它从未被修改过。尝试在代码中的任何地方修改它,你会得到一个编译错误(因为这会删除隐含的final)。

这被称为实际上是最终的。引用From the docs:

但是,从 Java SE 8 开始,本地类可以访问封闭块的局部变量和参数,它们是最终的或有效的最终的。一个变量或参数,其值在初始化后永远不会改变,它实际上是最终的。

【讨论】:

只要在任何地方(Runnable 之外)修改它就会出现错误,对吧? 是的,没错。把它想象成一个看不见的final Java 8潜行 100 嗨,有没有办法在 java 8 中有效地禁用 final (或在编译时显示警告)?我想精确地写'final'以避免代码中的错误引用【参考方案2】:

Java 8(和 Lambdas)引入了 Effectively final 术语:即使您没有使用 final 关键字将其定义为 final,但如果不修改,它与 final 一样好.

引用Oracle Tutorial: Local Classes:

但是,从 Java SE 8 开始,本地类可以访问封闭块的局部变量和参数,它们是最终的或实际上是最终的。一个变量或参数,其值在初始化后永远不会改变,它实际上是最终的。

您的消息实际上是最终的,因此可以从匿名内部类和 lambda 中引用它。

如果您更改消息的值,它将不再是有效的最终

String message = "Hello world";
new Runnable() 
    @Override
    public void run() 
        System.out.println(message);
    
.run();
message = "modified";

因此您会收到以下错误(来自 Eclipse):

在封闭范围内定义的局部变量消息必须是最终的或有效的最终

或形成javac:

错误:从内部类引用的局部变量必须是 final 或有效 final

【讨论】:

【参考方案3】:

变量message 是effectively final。引用语言参考

如果一个变量实际上是 final 的,将 final 修饰符添加到它的 声明不会引入任何编译时错误。

因此,因为 message 引用在您的内部类中的任何地方都没有更改,所以编译器将其视为有效的 final。

这会引发错误:

new Runnable() 
            @Override
            public void run() 
                message = "hey";
                System.out.println(message);
            
        .run();

java7 编译器抛出错误的原因是 lambdas 的规范更改。

使用的任何局部变量、形式参数或异常参数,但 不使用任何局部变量、形式参数或异常参数 但未在 lambda 表达式中声明的必须要么声明为 final 或有效地最终(§4.12.4),或发生编译时错误 尝试使用的地方。

匿名内部类和 lambda 共享相同的规则。

【讨论】:

【参考方案4】:

是的,有点像。

基本上,他们意识到编译器已经必须通过分析代码来决定何时局部变量是“有效最终的”,也就是说,它的值永远不会改变。

所以语义没有改变:虽然不再需要显式声明变量 final,但您仍然必须确保它永远不会被重新分配。

【讨论】:

只是附注:他们必须一直都知道——这与自那时以来一直产生final相关的编译器错误的分析完全相同Java 1.0 :) 在此期间发生了变化的是 Java 的精神——曾经是一种“蓝领语言”,其中样板和冗长是 特性,今天是一种具有全面的 FP 支持和语法糖。 @MarkoTopolnik 你是对的,他们意识到更多的是他们可以单独依靠它,没有理由强迫开发人员添加final 关键字。但从第一天起,答案就一直盯着他们。

以上是关于Java停止了内部类中非最终变量的错误(java 8)[重复]的主要内容,如果未能解决你的问题,请参考以下文章

java基础第九篇之final和内部类等

不能在不同方法中定义的内部类中引用非最终变量

Java之路 - final内部类ObjectDateCalendar

JAVA篇----内部类

Java基础学习笔记十 Java基础语法之finalstatic匿名对象内部类

06JAVA基础面向对象-继承/多态