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


    640?wx_fmt=jpeg

    作者:guanpj

    来源:https://www.jianshu.com/p/acc8d9a67d0c

    在开发过程中,由于习惯的原因,我们可能对某种编程语言的一些特性习以为常,特别是只用一种语言作为日常开发的情况。

    但是当你使用超过一种语言进行开发的时候就会发现,虽然都是高级语言,但是它们之间很多特性都是不太相同的。

    现象描述

    在  Java 8 之前,匿名内部类在使用外部成员的时候,会报错并提示 “Cannot refer to a non-final variable arg inside an inner class defined in a different method”

    640?wx_fmt=png

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

    640?wx_fmt=png

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

    640?wx_fmt=png

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

    640?wx_fmt=png

    原因分析

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

    640?wx_fmt=png

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

    class TestInnerClass$1 extends InnerClass {
        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);
        }
    }

    原来,匿名也会被当作普通的类处理,只不过编译器生成它构造方法的时候,除了将外部类的引用传递了过来,还将基本数据类型的变量复制了一份过来,并把引用数据类型的变量引用也传递了过来。

    因此,基本数据类型的变量当然不能修改了,不然就会跟外部的变量产生不一致,这样的话变量的传递也就变得毫无意义了。

    情景对比

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

    public final void useNestedClass(@NotNull final TestNestedClass.DataBean bean) {
      Intrinsics.checkParameterIsNotNull(bean, "bean");
      final IntRef num = new IntRef();//---1
      num.element = 1;//---2
      String var3 = "before action, num = " + num.element;
      System.out.println(var3);
      <undefinedtype> nestedClass = new TestNestedClass.NestedClass() {
       public void doSomething() {
        num.element = 678;//---3
        bean.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;//---4
      System.out.println(var4);
    }

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

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

    哈哈,并没有多此一举,点个赞!

    关注Java技术栈微信公众号,在后台回复关键字:Java,可以获取一份栈长整理的 Java 最新技术干货。

    最近干货分享

    公司不用 Spring Boot,果断离职了!

    Java 12 骚操作, 文件比对居然还能这样玩

    Java 12 骚操作, String居然还能这样玩

    Spring Boot 1.x 正式退役,2.x大步向前

    分享一份 2019 最新 Java 架构师学习资料

    640

    点击「阅读原文」加入栈长的战队~

  • 相关阅读:
    Python Twelfth Day
    Python Tenth Day
    Python Ninth Day
    Python Eighth Day
    Python Seventh Day
    Python Sixth Day
    Python Fifth Day
    Python Fourth Day
    Python Third Day
    金融量化分析-python量化分析系列之---使用python的tushare包获取股票历史数据和实时分笔数据
  • 原文地址:https://www.cnblogs.com/java-stack/p/11952128.html
Copyright © 2020-2023  润新知