重点理解Java引入内部类的原因以及好处
内部类能让你再逻辑上将相互从属的类组织起来,并且在类的内部控制访问权限。但是切记,内部类和合成时截然不同的,这一点非常重要。
几个有代表性的代码块
接口可以嵌套在类或其它接口中
接口的嵌套
1package edu.bupt.innerclass2;
2
3class A {
4 interface B {
5 void f();
6 }
7 public class BImp implements B {
8 public void f() {}
9 }
10 private class BImp2 implements B {
11 public void f() {}
12 }
13 public interface C {
14 void f();
15 }
16 class CImp implements C {
17 public void f() {}
18 }
19 private class CImp2 implements C {
20 public void f() {}
21 }
22 private interface D {
23 void f();
24 }
25 private class DImp implements D {
26 public void f() {}
27 }
28 public class DImp2 implements D {
29 public void f() {}
30 }
31 public D getD() { return new DImp2(); }
32 private D dRef;
33 public void receiveD(D d) {
34 dRef = d;
35 dRef.f();
36 }
37 }
38
39 interface E {
40 interface G {
41 void f();
42 }
43 // Redundant "public":
44 public interface H {
45 void f();
46 }
47 void g();
48 // Cannot be private within an interface:
49 //! private interface I {}
50 }
51
52 public class NestingInterface {
53 //B在A中定义的为默认级别
54 public class BImp implements A.B {
55 //public static int a;
56 public void f() {}
57 }
58
59 //这个内部类是编译不过的
60 /**//*
61 * No enclosing instance of type A is accessible to invoke the super constructor.
62 Must define a constructor and explicitly qualify its super constructor
63 invocation with an instance of A (e.g. x.super() where x is an instance of A).
64 */
65 public class BImpExtends extends A.BImp{
66
67 }
68 public class BImpExtendsCopy extends A.BImp{
69 public BImpExtendsCopy(A a){
70 a.super();
71 }
72 }
73 class CImp implements A.C {
74 public void f() {}
75 }
76 // Cannot implement a private interface except
77 // within that interface's defining class:
78 //! class DImp implements A.D {
79 //! public void f() {}
80 //! }
81 class EImp implements E {
82 public void g() {}
83 }
84 class EGImp implements E.G {
85 public void f() {}
86 }
87 class EImp2 implements E {
88 public void g() {}
89 class EG implements E.G {
90 public void f() {}
91 }
92 }
93 public static void main(String[] args) {
94 A a = new A();
95 // Can't access A.D:
96 // A.D ad = a.getD();
97 // Doesn't return anything but A.D:
98 //A.DImp2 di1=a.getD();
99 A.DImp2 di2 = (A.DImp2)a.getD();
100 di2.f();
101
102 // Cannot access a member of the interface:
103 //! a.getD().f();
104 //a.getD().f();
105 // Only another A can do anything with getD():
106 A a2 = new A();
107 a2.receiveD(a.getD());
108 }
109 }
110
返回内部类引用
1//: c08:Parcel2.java
2// Returning a reference to an inner class.
3
4public class Parcel2 {
5 class Contents {
6 private int i = 11;
7 public int value() { return i; }
8 }
9 class Destination {
10 private String label;
11 Destination(String whereTo) {
12 label = whereTo;
13 }
14 String readLabel() { return label; }
15 }
16 public Destination to(String s) {
17 return new Destination(s);
18 }
19 public Contents cont() {
20 return new Contents();
21 }
22 public void ship(String dest) {
23 Contents c = cont();
24 Destination d = to(dest);
25 System.out.println(d.readLabel());
26 }
27 public static void main(String[] args) {
28 Parcel2 p = new Parcel2();
29 p.ship("Tanzania");
30 Parcel2 q = new Parcel2();
31 // Defining references to inner classes:
32 Parcel2.Contents c = q.cont();
33 Parcel2.Destination d = q.to("Borneo");
34 }
35} ///:~
36
37除非是在"宿主类(outer class)"的非static方法里面,否则无论你再哪里创建内部类的对象,都必须OuterClassName.InnerClassName的形式来表示这个对象的类型,就像在main()里面那样
内部类在隐藏实现方面的作用
内部类隐藏机制
1
2但是,当你将它上转到基类时,特别是interface的时候,就会发现,内部类还是有它自己的特性的。这样,任何人都不能看到或者访问到内部类了--也就是interface的实现了,于是“隐藏实现”就轻而易举了。你所得到的只是一个基类或者interface的reference。
3当你拿到基类或interface的reference的时候,有可能你会没办法找出它的具体类型。
4如下所示
5package edu.b.innerclass2;
6//在类中定义的内部类 内部类可以是private protected public但是要注意其使用
7class Parcel3 {
8
9 private class PContents implements Contents{
10 private int i=11;
11 public int value(){
12 return i;
13 }
14 }
15
16 class PDestination implements Destination{
17 private String label;
18 public PDestination(){
19
20 }
21 private PDestination(String whereTo){
22 label=whereTo;
23 }
24 public String readLabel(){
25 return label;
26 }
27 }
28
29 public Contents cont(){
30 return new PContents();
31 }
32
33 public Destination dest(String s){
34 return new PDestination(s);
35 }
36
37}
38
39
40public class TestParcel {
41 public static void main(String[] args) {
42 Parcel3 p = new Parcel3();
43 Contents c = p.cont();
44 Destination d = p.dest("Tanzania");
45 // Illegal -- can't access private class:
46 //! Parcel3.PContents pc = p.new PContents();
47 }
48} ///:~
49在这个例子里,main()必须在另一个类里,这样才能让人看到内部类PContents的私密性方面的效果。
50Parcel3加了点新东西:内部类PContent是private的,所有除了Parcel3,谁都不能访问它。PDestination是protected的,因此除了Parcel3,同属这个package的类(因为protected也会给予包级权限),以及Parcel3的继承类,谁都不能访问PDestination。这就是说,客户程序员对这些成员的了解和访问权限是由限制的。实际上,你甚至不能将对象下转给private的内部类(或者是protected的内部类,除非你继承了这个类),因为,正如class TestParcel所演示的,你根本就不能用这个名字。由此“private的内部类”为类的设计者们提供了一种“能彻底杜绝【用具体类型来编程所引起的依赖性问题type-coding dependencies】,并且完全将实现细节隐藏起来”的方法 。此外,从客户程序员的角度来看,扩展interface也是毫无意义的,因为他根本没法访问public interface以外的方法。
51普通类(非内部类)是不能被定义成private或protected的;它们只可能是public 或package权限的
匿名内部类的几种使用形式
- A class defined within a method 在方法的内部定义一个类
- A class defined within a scope inside a method 在方法的某个作用域里定义一个类
- An anonymous class implementing an interface 一个实现了某个接口的匿名类
- An anonymous class extending a class that has a nondefault constructor 一个继承了“某个有着非默认构造函数”的类匿名类
- An anonymous class that performs field initialization一个进行数据成员初始化的匿名类
- An anonymous class that performs construction using instance initialization (anonymous inner classes cannot have constructors)一个通过实例初始化(匿名内部类不能有构造函数)来进行构建的匿名类
某个有着非默认构造函
1//: c08:Wrapping.java
2public class Wrapping {
3 private int i;
4 public Wrapping(int x) { i = x; }
5 public int value() { return i; }
6} ///
7
8// An anonymous inner class that calls
9// the base-class constructor.
10
11public class Parcel7 {
12 public Wrapping wrap(int x) {
13 // Base constructor call:
14 return new Wrapping(x) { // Pass constructor argument.
15 public int value() {
16 return super.value() * 47;
17 }
18 }; // Semicolon required
19 }
20 public static void main(String[] args) {
21 Parcel7 p = new Parcel7();
22 Wrapping w = p.wrap(10);
23 }
24} ///:~
对数据成员进行初始化的匿名类
1// An anonymous inner class that performs
2// initialization. A briefer version of Parcel4.java.
3
4public class Parcel8 {
5 // Argument must be final to use inside
6 // anonymous inner class:
如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译会要求其参数引用是final的,就像dest()的参数一样,否则不是final的,将会得到一个编译时错误信息
7 public Destination dest(final String dest) {
8 return new Destination() {
9 private String label = dest;
10 public String readLabel() { return label; }
11 };
12 }
13 public static void main(String[] args) {
14 Parcel8 p = new Parcel8();
15 Destination d = p.dest("Tanzania");
16 }
17} ///:~
实例初始化来构建匿名内部类
//: c08:AnonymousConstructor.java
// Creating a constructor for an anonymous inner class.
import com.bruceeckel.simpletest.*;
abstract class Base {
public Base(int i) {
System.out.println("Base constructor, i = " + i);
}
public abstract void f();
}
public class AnonymousConstructor {
private static Test monitor = new Test();
public static Base getBase(int i) {
return new Base(i) {
{
System.out.println("Inside instance initializer");
}
public void f() {
System.out.println("In anonymous f()");
}
};
}
public static void main(String[] args) {
Base base = getBase(47);
base.f();
monitor.expect(new String[] {
"Base constructor, i = 47",
"Inside instance initializer",
"In anonymous f()"
});
}
} ///:~
In this case, the variable i did not have to be final. While i is passed to the base constructor of the anonymous class, it is never directly used inside the anonymous class.
实例初始化来构建的匿名内部类
1// Using "instance initialization" to perform
2// construction on an anonymous inner class.
3
4public class Parcel9 {
5 private static Test monitor = new Test();
6 public Destination
7 dest(final String dest, final float price) {
8 return new Destination() {
9 private int cost;
10 // Instance initialization for each object:
11 {
12 cost = Math.round(price);
13 if(cost > 100)
14 System.out.println("Over budget!");
15 }
16 private String label = dest;
17 public String readLabel() { return label; }
18 };
19 }
20 public static void main(String[] args) {
21 Parcel9 p = new Parcel9();
22 Destination d = p.dest("Tanzania", 101.395F);
23 monitor.expect(new String[] {
24 "Over budget!"
25 });
26 }
27} ///:~
内部类与宿主类的关系
上面讲的内部类还只是一种隐藏名字和组织代码的方式,但是内部类还有一种用法。如果你创建了一个内部类,那么这个内部类的对象,就与创建它的
“宿主类的对象(enclosing object)”产生了某种联系,这样它就能访问宿主类对象的成员了---不需要任何特别的授权。
此外,内部类还能访问宿主类的所有元素。
内部类访问宿主类的成员
1package edu.bupt.innerclass2;
2
3interface Selector {
4 boolean end();
5
6 Object current();
7
8 void next();
9}
10
11public class Sequence {
12 private Object[] objects;
13
14 private int next = 0;
15
16 public Sequence(int size) {
17 objects = new Object[size];
18 }
19
20 public void add(Object x) {
21 if (next < objects.length)
22 objects[next++] = x;
23 }
24
25 private class SSelector implements Selector {
26 private int i = 0;
27
28 public boolean end() {
//return i == Sequence.this.objects.length;
// 在内部类中使用Sequence.this, 内部类对象中所指向宿主类的对象的reference
29 return i == objects.length;
30 }
31
32 public Object current() {
33 return objects[i];
34 }
35
36 public void next() {
37 if (i < objects.length)
38 i++;
39 }
40 }
41
42 public Selector getSelector() {
43 return new SSelector();
44 }
45
46 public static void main(String[] args) {
47 Sequence sequence = new Sequence(10);
48 for (int i = 0; i < 10; i++)
49 sequence.add(Integer.toString(i));
50 Selector selector = sequence.getSelector();
51 while (!selector.end()) {
52 System.out.println(selector.current());
53 selector.next();
54 }
55 }
56}
57
内部类里肯定会有一个指向“要负责创建它的”宿主类对象的reference。这样,当你引用宿主类的成员的时候,就会使用那儿(隐蔽的)reference来选取成员。编译器会为你打理着一切,但是你还是应该知道,
内部类对象的创建时与宿主类对象有关的。创建内部类对象的前提就是要获得宿主类对象的reference,如果编译器得不到这个引用,它就会报错,绝对大多数情况下,这个过程无需程序员的干预。
最好的方式就是通过在宿主类里的非static方法里定一个getInnerClassName(){new InnerClassName();}
在宿主类static方法及宿主类外定义内部类的对象
1// Creating instances of inner classes.
2
3public class Parcel11 {
4 class Contents {
5 private int i = 11;
6 public int value() { return i; }
7 }
8 class Destination {
9 private String label;
10 Destination(String whereTo) { label = whereTo; }
11 String readLabel() { return label; }
12 }
13 public static void main(String[] args) {
14 Parcel11 p = new Parcel11();
15 // Must use instance of outer class
16 // to create an instances of the inner class:
17 Parcel11.Contents c = p.new Contents();
18 Parcel11.Destination d = p.new Destination("Tanzania");
//但是如果创建的是嵌套类static innerclass的对象的话,就不需要宿主类对象的reference了
19 }
20} ///:~
Java嵌套类(static 内部类)和普通内部类的区别
如果你不需要这种“内部类对象和宿主类对象之间的”联系,那么你可以吧内部类定义为static的,这通常被称作“嵌套类nested class"。要想理解static用于内部类时的意思,你就必须记住,普通的内部类对象都默认保存“它的宿主类对象,也就是创建它的那个对象的reference。但是将内部类声明为static的时候,情况就不是这样了。嵌套类的意思是:
1,无需宿主类的对象就能创建嵌套类的对象。
2,不能在嵌套类的对象里面访问非static的宿主类对象
此外,嵌套类同普通的内部类还有一点不同。普通内部类的成员数据和方法只能到类的外围这一层,因此普通的内部类不能有static数据,static数据成员或嵌套类。但是这些东西static 嵌套类里都可以有。
但是如果创建的是嵌套类static innerclass的对象的话,就不需要宿主类对象的reference了
局部内部类
前面提到过,可以在代码块里创建内部类,典型的方式是在一个方法体的里面创建,
局部内部类不能有访问说明符,因为它不是外围类的一部分;但是它可以访问当前代码块内的常量final定义的.以及宿主类的所有成员。
局部内部类
1package innerrelationouter;
2
3import java.util.LinkedList;
4
5interface Counter {
6 int next();
7}
8
9public class LocalInnerClass {
10 private int count = 0;
11
12 Counter getCounter(final String name) {
13
14 // 必须定义为final的,否则内部类中不能访问
15 final String stringLocal = null;
16
17 // A local inner class
18 // Illegal modifier for the local class LocalCounter; only abstract or
19 // final is permitted
20 //LocalCounter的class的修饰符不能定义为private,protected,public
21 class LocalCounter implements Counter {
22
23 String test = stringLocal;
24
25 //The field staticVariable cannot be declared static; static fields can only be declared in static or top
26 //level types
27 //static String staticVariable="";
28
29 public LocalCounter() {
30 // Local inner class can have a constructor
31 System.out.println("LocalCounter()");
32 }
33
34 public int next() {
35 // Cannot refer to a non-final variable methodLocal inside an
36 // inner class defined in a different method
37 System.out.println(stringLocal);
38 System.out.print(name);// access local final,must be final
39 return count++; // 访问宿主类的成员
40 // return LocalInnerClass.this.count++;
41 }
42 }
43
44 return new LocalCounter();
45 }
46
47 // The same thing happens in the anoymous inner class
48 Counter getCounter2(final String name) {
49 // 必须定义为final的,否则内部类中不能访问
50 final String localString = "ddd";
51
52 return new Counter() {
53 String test;
54 // Anonymous inner class cannot have a named
55 // construtor,only an instance initializer:
56 {
57 // Cannot refer to a non-final variable localString inside an
58 // inner class defined in a different method
59 System.out.println(localString);
60 System.out.println("Counter()");
61 }
62
63 public int next() {
64 System.out.print(name);// access local final
65 return count++;
66 }
67 };
68 }
69
70 public static void main(String[] args) {
71 LocalInnerClass lic = new LocalInnerClass();
72 Counter c1 = lic.getCounter("Local inner"), c2 = lic
73 .getCounter2("Annoumous inner");
74
75 for (int i = 0; i < 5; i++)
76 System.out.println(c1.next());
77 for (int i = 0; i < 5; i++)
78 System.out.println(c2.next());
79
80 }
81
82}
83
继承内部类
如下,可以看到InheritInner继承的只是内部类,而不是它的宿主类。但是等到要创建构造函数的时候,默认的构造函数玩不转了,你必须传给他宿主类对象的reference。此外,你还必须在构造函数里面使用这种语法 enclosingClassReference.super();
继承内部类
1Inheriting from inner classes
2
3Because the inner class constructor must attach to a reference of the enclosing class object, things are slightly complicated when you inherit from an inner class. The problem is that the “secret” reference to the enclosing class object must be initialized, and yet in the derived class there’s no longer a default object to attach to. The answer is to use a syntax provided to make the association explicit: Feedback
4
5
6//: c08:InheritInner.java
7// Inheriting an inner class.
8
9class WithInner {
10 class Inner {}
11}
12
13public class InheritInner extends WithInner.Inner {
14 //! InheritInner() {} // Won't compile
15 InheritInner(WithInner wi) {
16 wi.super();
17 }
18 public static void main(String[] args) {
19 WithInner wi = new WithInner();
20 InheritInner ii = new InheritInner(wi);
21 }
22} ///:~
23
24
25
26You can see that InheritInner is extending only the inner class, not the outer one. But when it comes time to create a constructor, the default one is no good, and you can’t just pass a reference to an enclosing object. In addition, you must use the syntax Feedback
27
28
29enclosingClassReference.super();
30
为什么要使用内部类
在一定程度上解决Java中多重继承的问题
内部类解决多重继承问题
1//: c08:MultiInterfaces.java
2// Two ways that a class can implement multiple interfaces.
3
4interface A {}
5interface B {}
6
7class X implements A, B {}
8
9class Y implements A {
10 B makeB() {
11 // Anonymous inner class:
12 return new B() {};
13 }
14}
15
16public class MultiInterfaces {
17 static void takesA(A a) {}
18 static void takesB(B b) {}
19 public static void main(String[] args) {
20 X x = new X();
21 Y y = new Y();
22 takesA(x);
23 takesA(y);
24 takesB(x);
25 takesB(y.makeB());
26 }
27} ///:~
28
29
30--------------------------------------------
但是如果你碰上的不是interface而是abstract类或实体类,而且还一定要同时实现这两个类的话,那么你只能使用内部类了:
31//: c08:MultiImplementation.java
32// With concrete or abstract classes, inner
33// classes are the only way to produce the effect
34// of "multiple implementation inheritance."
35package c08;
36
37class D {}
38abstract class E {}
39
40class Z extends D {
41 E makeE() { return new E() {}; }
42}
43
44public class MultiImplementation {
45 static void takesD(D d) {}
46 static void takesE(E e) {}
47 public static void main(String[] args) {
48 Z z = new Z();
49 takesD(z);
50 takesE(z.makeE());
51 }
52} ///:~
53
54
55
即使无需解决”多重实现的继承multiple implementation inheritance)"这类问题,那么即使没有内部类,你也完全可以解决问题。但是有了内部类,你就得到如下的附加特性:
1,内部类可以有多个实例,而每个又都可以有它自己的,与宿主类对象无关的状态信息
2,一个宿主类里可以放上好几个内部类,它们可以用各自不同的方式来实现同一个interface或继承同一个类。举例来说,要是Sequence.java没有使用内部类,那么牛就只能说"Sequence是一个Selector”,于是每个Sequence里面就只能邮购一个Selector。但是现在,你可以很方便地再定义一个getRSelector()方法,让它返回一个会倒过来访问这个序列的Selector。显然只有内部类才能提供这种灵活性。
3,内部类对象创建的时机与宿主类对象的创建没有什么关系。
4,内部类不存在什么让人头晕的“是”关系;他是一个独立的实体
闭包和回调
闭包和回调
1//: c08:Callbacks.java
2// Using inner classes for callbacks
3import com.bruceeckel.simpletest.*;
4
5interface Incrementable {
6 void increment();
7}
8
9// Very simple to just implement the interface:
10class Callee1 implements Incrementable {
11 private int i = 0;
12 public void increment() {
13 i++;
14 System.out.println(i);
15 }
16}
17
18class MyIncrement {
19 void increment() {
20 System.out.println("Other operation");
21 }
22 static void f(MyIncrement mi) { mi.increment(); }
23}
24
25// If your class must implement increment() in
26// some other way, you must use an inner class:
27class Callee2 extends MyIncrement {
28 private int i = 0;
29 private void incr() {
30 i++;
31 System.out.println(i);
32 }
33 private class Closure implements Incrementable {
34 public void increment() { incr(); }
35 }
36 Incrementable getCallbackReference() {
37 return new Closure();
38 }
39}
40
41class Caller {
42 private Incrementable callbackReference;
43 Caller(Incrementable cbh) { callbackReference = cbh; }
44 void go() { callbackReference.increment(); }
45}
46
47public class Callbacks {
48 private static Test monitor = new Test();
49 public static void main(String[] args) {
50 Callee1 c1 = new Callee1();
51 Callee2 c2 = new Callee2();
52 MyIncrement.f(c2);
53 Caller caller1 = new Caller(c1);
54 Caller caller2 = new Caller(c2.getCallbackReference());
55 caller1.go();
56 caller1.go();
57 caller2.go();
58 caller2.go();
59 monitor.expect(new String[] {
60 "Other operation",
61 "1",
62 "2",
63 "1",
64 "2"
65 });
66 }
67} ///:~
68
这段程序还进一步揭示了“让宿主类去实现接口”和“交给内部类去做”之间的区别
Callee2继承了MyIncrement,而MyIncrement已经包括了一个它自己的increment(),而且这个方法的功能同Incrementable接口所定义的毫无相关。所以Callee2继承MyIncrement之后,就不能靠实现Incrementable接口来覆写increment()了,这就逼着你只能使用内部类来提供一个独立的实现了。
注意一下创建匿名内部类的不一定一定是Interface和abstract class,具体的类也可以,比如new Thread(){run(){}}.start();
posted on 2009-05-24 22:52
Frank_Fang 阅读(1013)
评论(0) 编辑 收藏 所属分类:
Java编程