终于明白为什么要加 final 关键字了!

时间:2021-7-3 作者:qvyue

1  现象描述

Java 8 之前,匿名内部类在使用外部成员的时候,会报错并提示 

“Cannot refer to a non-finalvariable arg inside aninnerclassdefinedinadifferentmethod”:

终于明白为什么要加 final 关键字了!

但是在 Java 8 之后,类似场景却没有再提示了:

终于明白为什么要加 final 关键字了!

难道是此类变量可以随便改动了吗?当然不是,当你试图修改这些变量的时候,仍然会提示错误:

终于明白为什么要加 final 关键字了!

可以看到,当试图修改基本数据类型的变量时,编译器的警告变成了 “Varible ‘num’ is accessed from within inner class, need to be final or effectively final”,很遗憾,仍然不能修改。相比之下,Kotlin 是没有这个限制的:

终于明白为什么要加 final 关键字了!

2  原因分析

从表面上当然看不出什么原因,看看编译器做了什么工作吧!运行 javac 命令后生成了几个 .class 文件:

终于明白为什么要加 final 关键字了!

不难推断,这个 TestInnerClass$1.class 就是匿名内部类编译后的文件,看看它反编译后是什么内容:

classTestInnerClass$1extendsInnerClass{TestInnerClass$1(TestInnerClass var1, int var2, DataBean var3) {super(var1);this.this$0= var1;this.val$num = var2;this.val$bean = var3;    }    void doSomething() {super.doSomething();System.out.println(“num = “+this.val$num);System.out.println(“bean name is: “+this.val$bean.name);    }}

原来,匿名也会被当作普通的类处理,只不过编译器生成它构造方法的时候,除了将外部类的引用传递了过来,还将基本数据类型的变量复制了一份过来,并把引用数据类型的变量引用也传递了过来。因此,基本数据类型的变量当然不能修改了,不然就会跟外部的变量产生不一致,这样的话变量的传递也就变得毫无意义了。

3  情景对比

但是为什么对于 Kotlin 来说可以在匿名内部类中直接修改基本数据类型的值呢?查看 Kotlin 编译后反编译回来的内容:

publicfinalvoiduseNestedClass(@NotNull final TestNestedClass.DataBean bean){Intrinsics.checkParameterIsNotNull(bean,”bean”);final IntRef num =newIntRef();//—1num.element =1;//—2String var3 =”before action, num = “+ num.element;System.out.println(var3); nestedClass =newTestNestedClass.NestedClass() {publicvoiddoSomething(){num.element =678;//—3bean.setName(“xyz”);String var1 =”num = “+ num.element;System.out.println(var1);var1 =”bean name is: “+ bean.getName();System.out.println(var1);        }      };      nestedClass.doSomething();String var4 =”after action, num = “+ num.element;//—4System.out.println(var4);  }

可以发现,当需要传递基本数据类型的变量时,Kotlin 编译器会将这些数据进行包装,从而由值传递变为引用传递,这样内部的修改当然就不会影响到外部了。

验证一下,当变量不进行传递时,Kotlin 编译器是怎么处理的: 

publicfinalvoiduseNestedClass(@NotNull TestNestedClass.DataBean bean){Intrinsics.checkParameterIsNotNull(bean,”bean”);intnum =1;String var3 =”before action, num = “+ num;System.out.println(var3);intnum =678;var3 =”after action, num = “+ num;System.out.println(var3);  }

参考文章:https://mp.weixin.qq.com/s/nPYOa5pmseuLdIKOewxF5g

声明:本文内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:qvyue@qq.com 进行举报,并提供相关证据,工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。