标签:
Java 匿名内部类
前边一篇文章对匿名内部类做了一个简单的了解,http://my.oschina.net/xinxingegeya/blog/297004。这篇文章来深入的了解一下匿名内部类的使用
ava的匿名类特性,在于可以在项目里“内联”地实现一个类型,它可以继承一个现有的具体或抽象类,或是实现接口,并提供完整的成员实现。例如,这里有个抽象类,定义了一个抽象方法:
// 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定义,即不可改变的引用。
private final Integer c = 12; // 必须为final
使用匿名内部类时,我们必须是继承一个类或者实现一个接口,但是两者不可兼得,同时也只能继承一个类或者实现一个接口。
匿名内部类中是不能定义构造函数的。
匿名内部类中不能存在任何的静态成员变量和静态方法。
匿名内部类为局部内部类,所以局部内部类的所有限制同样对匿名内部类生效。
匿名内部类不能是抽象的,它必须要实现继承的类或者实现的接口的所有抽象方法。
参考引用:http://www.cnblogs.com/chenssy/p/3390871.html
http://blog.zhaojie.me/2011/06/java-anonymous-method-closure-scope-this.html
=======================END=======================
标签:
原文地址:http://my.oschina.net/xinxingegeya/blog/490892