Fork me on GitHub

Java编程思想———内部类

可以将一个类的定义放到另一个类的定义内部,这就是内部类。

链接到外部类

当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种联系,所以它能访问其外围对象的所有成员,而不需要任何特殊条件。此外,内部类还拥有其外围类的所有元素的访问权。

使用.this与.new

如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟.this

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class DotThis {
void f() {
System.out.println("DotThis.f()");
}
public class Inner {
public DotThis outer() {
return DotThis.this;
}
}
public Inner inner() {
return new Inner();
}
public static void main(String[] args) {
DotThis dt = new DotThis();
DotThis.Inner dti = dt.inner();
dti.outer().f();
}
}

//output
DotThis.f()

由外部类对象去创建其某个内部类的对象,必须在new表达式中提供外部对象的引用,这需要使用.new语法。

1
2
3
4
5
6
7
public class DotNew {
public class Inner {}
public static void main(String[] args) {
DotNew dn = new DotNew();
DotNew.Inner dni = dn.new Inner();
}
}

在拥有外部类对象之前是不可能创建内部类对象的。这是因为内部类对象会暗暗地连接到创建它的外部类对象上。但是,如果创建的是嵌套类(静态内部类),那么它就不需要对外部类对象的引用。

匿名内部类

如果定义一个匿名内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。

无参构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public class Contents {
piblic int i;
public int value() {
return i;
}
}

public class ParCel7 {
public Contents contents() {
return new Contents() { //无参构造
private int i = 11;
private int value() { return i; }
};
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents();
}
}

有参构造

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class Wrapping {
piblic int i;
public Wrapping(int x) {
return i = x;
}
public int value() {
return i;
}
}

public class Parcel8 {
public Wrapping wrapping(int x) {
return new Wrapping(x) { //有参构造
public int value() {
return super.value() * 47;
}
}
}
public static void main(String[] args){
Pracel8 p = new Pracel8();
Wrapping w = p.wrapping(10);
}
}

带实例初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public Parcel10 {
public Destination destination(final String dest, final float price) {
return new Destination() {
private int cost;
{
cost = Math.round(price); //实例初始化
if(cost > 100){
System.out.println("Over budget!");
}
}
private String label = dest; //实例初始化
public String readLabel() {
return label;
}
}
}
public static void main(String[] args){
Parcel10 p = new Parcel10();
Destination d = p.destination("Tasmania", 101.395F);
}
}

//output
Over budget!

嵌套类(静态内部类)

如果将内部类声明为static,则称为嵌套类。

  • 要创建嵌套类的对象,并不需要其外围类的对象。
  • 不能从嵌套类的对象中访问非静态的外围类对象。

接口内部的类

因为接口中的任何类都自动地是publicstatic的,因此接口中的类是嵌套类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface {
public void howdy() {
System.out.println("Howdy!");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}

//output
Howdy!

从多层嵌套类中访问外部类的成员

一个内部类被嵌套多少层并不重要,它能透明地访问所有它所嵌入的外围类的所有成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
class MNA {
public void f();
class A {
private void g() {}
public class B {
void h() {
g();
f();
}
}
}
}

public class MultiNestingAccess {
public static void main(String[] args) {
MNA mna = new MNA();
MNA.A mnaa = mna.new A();
MNA.A.B mnaab = mnaa.new B();
mnaab.h();
}
}

内部类的继承

1
2
3
4
5
6
7
8
9
10
11
12
13
class WithInner {
class Inner {}
}

public class InheritInner extends WithInner.Inner {
InheritInner(WithInner wi){ //有参构造
wi.super(); //重要
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}

不能使用默认构造器,必须使用有参构造器,需要传递一个指向外围类对象的引用,同时必须在构造器内使用如下语法:
enclosingClassReference.super(),这样才提供了必要的引用。

内部类可以被覆盖吗

覆盖内部类就好像它是外围类的一个方法,其实并不起什么作用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
class Egg {
private Yolk y;
protected class Yolk {
public Yolk() {
System.out.println("Egg.Yolk()");
}
}
public Egg() {
System.out.println("New Egg()");
y = new Yolk();
}
}

public class BigEgg extends Egg {
public class Yolk {
public Yolk() {
System.out.println("BigEgg.Yolk()");
}
}
public static void main(String[] args) {
new BigEgg();
}
}

//output
New Egg()
Egg.Yolk()

当继承了某个外围类的时候,内部类并没有发生什么特别神奇的变化。这两个内部类是完全独立的两个实体,各自在自己的命名空间内。

但是,可以明确地继承某个内部类。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
class Egg2 {
protected class Yolk {
public Yolk() {
System.out.println("Egg2.Yolk()");
}
public void f() {
System.out.println("Egg2.Yolk.f()");
}
}
private Yolk y = new Yolk();
public Egg2() {
System.out.println("New Egg2()");
}
public void insertYolk(Yolk yy) {
y = yy;
}
public void g() {
y.f();
}
}

public class BigEgg2 extends Egg2 {
public class Yolk extends Egg2.Yolk {
public Yolk() {
System.out.println("BigEgg2.Yolk()");
}
public void f() {
System.out.println("BigEgg2.Yolk.f()");
}
}
public BigEgg2() {
insertYolk(new Yolk());
}
public static void main(String[] args) {
Egg2 e2 = new BigEgg2();
e2.g();
}
}

//output
Egg2.Yolk()
New Egg2()
Egg2.Yolk()
BigEgg2.Yolk()
BigEgg2.Yolk.f()

局部内部类

可以在代码块或者方法体内创建内部类,叫做局部内部类。局部内部类不能有访问说明符,因为它不是外围类的一部分;但是它可以访问当前代码块内的常量,以及此外围类的所有成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
interface Counter {
int next();
}

public class LocalInnerClass {
private int count = 0;
Counter getCounter(final String name) {
//局部内部类
class LocalCounter implements Counter {
public LocalCounter() {
System.out.println("LocalCounter()");
}
public int next() {
System.out.println(name);
return count++;
}
}
return new LocalCounter();
}
Counter getCounter2(final String name) {
//匿名内部类
return new Counter() {
{
System.out.println("Counter()");
}
public int next() {
System.out.println(name);
return count++;
}
}
}
public static void main(String[] args) {
LocalInnerClass lic = new LocalInnerClass();
Counter c1 = lic.getCount("Local inner ");
Counter c2 = lic.getCount2("Anonymous inner ");
for(int i = 0; i < 5; i++){
System.out.println(c1.next);
}
for(int i = 0; i < 5; i++){
System.out.println(c2.next);
}
}
}

//output
LocalCounter()
Counter()
Local inner 0
Local inner 1
Local inner 2
Local inner 3
Local inner 4
Anonymous inner 5
Anonymous inner 6
Anonymous inner 7
Anonymous inner 8
Anonymous inner 9
求鼓励,求支持!