文档章节

Java匿名内部类

秋风醉了
 秋风醉了
发布于 2015/08/11 19:09
字数 1177
阅读 128
收藏 0

Java匿名内部类

前边一篇文章对匿名内部类做了一个简单的了解,http://my.oschina.net/xinxingegeya/blog/297004

这篇文章来深入的了解一下匿名内部类的使用


Java匿名类中的this

java的匿名类特性,在于可以在项目里“内联”地实现一个类型,它可以继承一个现有的具体或抽象类,或是实现接口,并提供完整的成员实现。例如,这里有个抽象类,定义了一个抽象方法:

// Java
abstract class MyAbstractClass {
    String getName() {
        return "MyAbstractClass";
    }
    abstract void print();
}

然后我们在另一个地方使用一个匿名类,继承这个类:

// Java
class MyClass {
    String getName() {
        return "MyClass";
    }
    MyAbstractClass someMethod() {
        return new MyAbstractClass() {
            public void print() {
                System.out.println(getName());
            }
        };
    }
}

好,现在提一个问题,运行下面这行代码会打印出什么结果?

// Java
new MyClass().someMethod().print();

输出结果是MyAbstractClass而不是MyClass。换句话说,匿名类型中调用的getName方法是定义在MyAbstractClass里的,而不是定义在词法作用域(Lexical Scope)里的getName方法。根据Java规范,匿名类中的this(包括上面代码中“隐式”的this)表示类型本身对象,而与上下文无关。如果要访问词法作用域里的getName方法(即MyClass的方法),则反而必须显式指定MyClass类 ,例如:

// Java
class MyClass {
    String getName() {
        return "MyClass";
    }
    MyAbstractClass someMethod() {
        return new MyAbstractClass() {
            public void print() {
                System.out.println(MyClass.this.getName());
            }
        };
    }
}


匿名内部类定义属性字段

如下是匿名内部类的例子,

package com.usoft.java;

/**
 */
public interface CallBack {
    void call();
}
package com.usoft.java;

/**
 * @author: Lenovo(2015-08-09 16:00)
 */
public class Test {

    private final Integer c = 12;
    private Integer a;
    private Integer b;

    public Test(Integer a, Integer b) {
        this.a = a;
        this.b = b;
    }

    public static void main(String args[]) {
        Test test = new Test(1, 2);
        test.test(true);
    }

    public void hello(CallBack back) {
        back.call();
    }

    public void test(final boolean flag) {
        System.out.println("test");
        hello(new CallBack() {
            private Integer ia = a;
            private Integer ib = b;

            @Override
            public void call() {
                System.out.println("callback");
                if (flag)
                    System.out.println(ia + ib + c);
                else
                    System.out.println(ia + ib + c + 1);
            }
        });
    }
}

匿名内部类虽然说是匿名,但编译后也是有名字的。看一下匿名内部类的反编译结果,类的名字是 Test$1,和普通的类没有什么区别。

C:\WorkSpace5-gitosc\scala-sample\out\production\scala-sample\com\usoft\java>javap -p Test$1.class
Compiled from "Test.java"
class com.usoft.java.Test$1 implements com.usoft.java.CallBack {
  private java.lang.Integer ia;
  private java.lang.Integer ib;
  final boolean val$flag;
  final com.usoft.java.Test this$0;
  com.usoft.java.Test$1(com.usoft.java.Test, boolean);
  public void call();
}

在匿名内部类中定义了两个属性 ia 和 ib,ia 和 ib 的初始化和赋值是在匿名内部类的构造函数中完成的,值是外部类中a 和 b。在匿名内部类中使用了外部的变量 flag。

其中初始化和赋值 ia 和 ib的时候更明显和更好理解的写法是,

public void test(final boolean flag) {
    System.out.println("test");
    hello(new CallBack() {
        private Integer ia = Test.this.a;
        private Integer ib = Test.this.b;

        @Override
        public void call() {
            System.out.println("callback");
            if (flag)
                System.out.println(ia + ib + c);
            else
                System.out.println(ia + ib + c + 1);
        }
    });
}

运行结果,

test

callback

15


匿名内部类的初始化

由于匿名内部类没有构造函数(通过反编译你可以知道是有构造函数的),那么怎么初始化匿名内部类,下面是一种方法,

public void test(final boolean flag) {
    System.out.println("test");
    hello(new CallBack() {
        private Integer ia = Test.this.a;
        private Integer ib = Test.this.b;

        {
            ia = 3;
            ib = 4;
        }

        @Override
        public void call() {
            System.out.println("callback");
            if (flag)
                System.out.println(ia + ib + c);
            else
                System.out.println(ia + ib + c + 1);
        }
    });
}

通过代码块,可以初始化类的属性,打印的结果为 19;


为什么使用final

通过上面的反编译结果,我们可以看到,内部类的构造函数(字节码层面)中有一个外部类参数,同时内部类中有一个外部类的引用,当使用外部类的属性时,是通过外部类的引用得到的相应的属性值。如果外部类的属性引用改变,那么内部类和外部类状态就不统一了。所以为了状态的统一,规定所有的外部类的属性为final定义,即不可改变的引用。

private final Integer c = 12; // 必须为final


匿名内部类的使用总结

  1. 使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。

  2. 匿名内部类中是不能定义构造函数的。

  3. 匿名内部类中不能存在任何的静态成员变量和静态方法。

  4. 匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。

  5. 匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。

参考引用:http://www.cnblogs.com/chenssy/p/3390871.html

http://blog.zhaojie.me/2011/06/java-anonymous-method-closure-scope-this.html

=================END=================

© 著作权归作者所有

上一篇: Scala Traits
秋风醉了
粉丝 246
博文 543
码字总数 412294
作品 0
朝阳
程序员
私信 提问
在 Java 中初始化 List 的五种方法

在 Java 中初始化 的五种方法 1.构造 后使用 初始化 2.使用 双括号语法 3.使用 4. 使用 (JDK8) 5. 使用 (JDK9) 参考 在 Java 中初始化 的五种方法 Java 中经常需要使用到 List,下面简单介...

yzbyzz
2018/12/05
0
0
Java中的内部类实例初始化匿名函数(JLS7 8.6:Instance Initializers)

偶尔发现了一段好玩的java代码,关于Java内部类的,觉得很有意思,但是想不明白这段代码是java语法的特性哪?还是别的什么。最后发现还是对java了解的不够深啊。先上代码: import java.util...

qalong
2013/09/02
0
0
Java static和匿名内部类

-- static Java中,任何变量 / 代码存储时,都是 在编译时 由系统自动分配内存; 在静态变量编译后,所分配的内存会一直存在,直到程序退出内存才会释放这个空间; 类加载时,JVM会把静态变量...

desaco
2018/10/27
0
0
【目录导航】JAVA零基础进阶之路

【JAVA零基础入门系列】(已完结)导航目录 Day1 开发环境搭建 Day2 Java集成开发环境IDEA Day3 Java基本数据类型 Day4 变量与常量 Day5 Java中的运算符 Day6 Java字符串 Day7 Java输入与输出...

MFrank
2018/06/21
0
0
黑马程序员---面向对象之内部类

-------------------------android培训、java培训、期待与您交流! ------------------------- Java中的内部类有四种:一般内部类、静态内部类、局部内部类、匿名内部类。 一、一般内部类 定...

长平狐
2013/07/01
82
0

没有更多内容

加载失败,请刷新页面

加载更多

趣谈预留实例券,一文搞懂云上省钱最新玩法

摘要: 一文搞懂时髦的预留实例券(RI) ECS近期推出了预留实例券(Reserved Instances),简称RI,这东西很cool,今天我们聊聊这个。 首先这篇Blog不是文档,读完后想详细了解文档的朋友请点...

zhaowei121
5分钟前
0
0
js 将json字符串转换为json对象的方法解析

js 将json字符串转换为json对象的方法解析 将json字符串转换为json对象的方法。在数据传输过程中,json是以文本,即字符串的形式传递的,而JS操作的是JSON对象,所以,JSON对象和JSON字符串之...

城市之雾
5分钟前
0
0
国外青少年最喜爱的聊天 app,竟然是 Google Docs

简评: 这还真不是标题党,Google Docs 的协作中内置了实时聊天的功能,也可以进行 comments,颇有种现代「传纸条」的既视感。其实国内的石墨文档、腾讯文档等协作工具也有类似的效果,本文很...

极光推送
10分钟前
0
0
用zuul将微服务的多个swagger api文档聚合成一个文档

1.在每个服务的pom中添加以下依赖 <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId></dependency> 注意:仅仅需要添加这个就行。zuul负......

xtof
11分钟前
0
0
为什么取不到metamask的账号?

当你使用Metamask测试以太坊DApp时,如果出现莫名其妙的问题,检查一下web3.eth.accounts是否可以获取到账户,如果不能获取的话,那么最大的可能是你使用了新版的Metamask,并且默认启用了隐...

汇智网教程
12分钟前
0
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部