第十章:内部类
1、内部类表面上看就是一种代码隐藏机制
2、其实内部类能与外部类通信,并且含有一个外部类的引用,所以能访问外部类所有的成员,包括private
3、内部类解决一部分的语言设计问题;类具有单继承的局限性,接口可以多继承、多实现,当然类也能多重继承(A继承B,B继承C);
然而多个内部类可以继承或实现多个接口,保证了语言的完整性。
一、链接外部类
内部类有权访问内部类所有成员,包括私有化成员,因为内部类存有外部类的引用
public class Parcel2 {
private int i = 2;
private void println(){System.out.println(i);}
class Contents {
private int i = 11;
public int value() { return i; }
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() {
println();//访问外部类私有化成员方法
return label+"_访问外部类私有化成员变量i="+i; }
}
public Destination to(String s) {
return new Destination(s);
}
public Contents contents() {
return new Contents();
}
public void ship(String dest) {
Contents c = contents();
Destination d = to(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel2 p = new Parcel2();
p.ship("Tasmania");
Parcel2 q = new Parcel2();
// Defining references to inner classes:
Parcel2.Contents c = q.contents();
Destination d = q.to("Borneo");
System.out.println(c.i);//本类中,允许访问内部类私有化成员
}
} /* Output:
2
Tasmania_访问外部类私有化成员i=2
11
*///:~
二、非静态成员位置内部类
1、用 OutClass.InnerClass 表示内部类类型,在1.8后直接可以使用InnerClass表示内部类类型。
2、创建内部类,先要创建外部类,利用外部类的引用创建内部类,例如:
OuterClass out = new OutClass();
OutClass.InnerClass = out.new InnerClass();
3、非静态成员内部了理解为成员位置的非静态方法,所以要使用内部类必须先创建外部类的引用。
public class Parcel2 {
class Contents {
private int i = 11;
public int value() { return i; }
}
class Destination {
private String label;
Destination(String whereTo) {
label = whereTo;
}
String readLabel() { return label; }
}
public Destination to(String s) {
return new Destination(s);
}
public Contents contents() {
return new Contents();
}
public void ship(String dest) {
Contents c = contents();
Destination d = to(dest);
System.out.println(d.readLabel());
}
public static void main(String[] args) {
Parcel2 p = new Parcel2();
p.ship("Tasmania");
Parcel2 q = new Parcel2();
// Defining references to inner classes:
Contents c = q.contents(); //也可以Parcel2.Contents c = q.contents();或者q.new Contents();
Destination d = q.to("Borneo");
}
} /* Output:
Tasmania
*///:~
三、静态成员位置内部类(也称 嵌套类)
1、静态内部类不存在外部类的引用,随类的加载而初始化,所以不能直接访问外部类的非静态成员。
2、返回值类型直接使用 InnerClass类型
3、静态成员内部类会编译OuterClass$InnerClass.class文件。
public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface {//默认public static修饰
public void howdy() {
System.out.println("Howdy!");
}
public static void main(String[] args) {
new Test().howdy();
}
}
} /* Output:
Howdy!
*///:~
作用:静态内部类做测试使用,不会写太多文件,也不用从新编译代码
public class TestBed {
public void f() { System.out.println("f()"); }
public static class Tester {
public static void main(String[] args) {
TestBed t = new TestBed();
t.f();
}
}
} /* Output:
f()
*///:~
四、使用this和new
OuterClass.this用在内部类中表示外部类的引用,从而调用外部类的成员。
public class DotThis {
void f() { System.out.println("DotThis.f()"); }
public class Inner {
public DotThis outer() {
// 这里使用外部类.this表示使用外部类对象的引用
return DotThis.this;
}
}
public Inner inner() { return new Inner(); }
public static void main(String[] args) {
DotThis dt = new DotThis();
Inner dti = dt.inner();
// Inner dti = dt.new Inner(); 这种写法和上面一样有效,可以非静态成员内部了理解为成员位置的非静态方法
dti.outer().f();
}
} /* Output:
DotThis.f()
*///:~
五、内部类向上转型
1、内部类被private修饰表示私有化成员,只能本类中访问;这样具有隐蔽性
2、被protected修饰的内部类只能本包中或子类访问。
3、内部类提升为基类型指向子类型,调用子类覆盖的方法。
package innerclasses;
class Parcel4 {
private class PContents implements Contents {
private int i = 11;
public int value() { return i; }
}
protected class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
}
public Destination destination(String s) {
return new PDestination(s);
}
public Contents contents() {
return new PContents();
}
}
public class TestParcel {
public static void main(String[] args) {
Parcel4 p = new Parcel4();
Contents c = p.contents();
//System.out.println(c.i); 这种是不允许的,因为成员已经是私有化的
Destination d = p.destination("Tasmania");
// 如下代码是错误的,因为该成员被修饰了private、protected,类外无法被访问
// Parcel4.PContents pc = p.new PContents();
// Parcel4.PDestination = p.new PDestination();
}
} ///:~
六、局部内部类(方法和作用域中的内部类)
1、为了解决某些问题,又不想这个类被公用
2、局部内部类它的生命周期随方法或者作用域一样,方法执行完,生命周期就结束。
方法中的内部类:
public class Parcel5 {
public Destination destination(String s) {
class PDestination implements Destination {
private String label;
private PDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
}
return new PDestination(s);//返回Destination引用指向PDestination子类
}
public static void main(String[] args) {
Parcel5 p = new Parcel5();
Destination d = p.destination("Tasmania");
}
} ///:~
作用域中的内部类:
public class Parcel6 {
private void internalTracking(boolean b) {
if(b) {
class TrackingSlip {
private String id;
TrackingSlip(String s) {
id = s;
}
String getSlip() { return id; }
}
TrackingSlip ts = new TrackingSlip("slip_作用域中内部类");
String s = ts.getSlip();
System.out.println(s);
}
// 超出作用域,无效
//! TrackingSlip ts = new TrackingSlip("x");
}
public void track() { internalTracking(true); }
public static void main(String[] args) {
Parcel6 p = new Parcel6();
p.track();
}
} /* Output:
slip_作用域中内部类
*///:~
七、匿名内部类
默认构造的匿名内部类:
public class Parcel7 {
public Contents contents() {
return new Contents() { // 返回Contents的子类型
private int i = 11;
public int value() { return i; }
}; // 分号结尾
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents();
}
}
带参数构造的匿名内部类:
1、匿名内部类想做一些类似构造器的效果:如下使用代码块,初始化时调用,就有效果了。
2、匿名内部类没有直接使用一个外部定义的对象,所以不要使用final修饰;当然jdk1.8后可以不使用final修饰也行。
public interface Destination {
String readLabel();
}
abstract class Base {
public Base(int i) { //没有使用final修饰
print("Base constructor, i = " + i);
}
public abstract void f();
}
public class Parcel10 {
//由于外部定义的变量被匿名内部类的基类使用,而没有直接使用,所以不用使用final修饰
public static Base getBase(int i) {
return new Base(i) { // 带参数的构造器
{ print("Inside instance initializer"); }//代码块
public void f() {
print("In anonymous f()");
}
};
}
// 匿名内部类需要直接使用了一个外部定义的局部变量,需加final修饰
public Destination destination(final String dest, final float price) {
return new Destination() {
private int cost;
// Instance initialization for each object:
{
cost = Math.round(price);
if(cost > 100)
System.out.println("Over budget!"+cost);
}
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);
System.out.println(d.readLabel());
System.out.println("======静态内部类=======");
Base base = getBase(47);
base.f();
}
} /* Output:
Over budget!
Tasmania
======静态内部类=======
Base constructor, i = 47
Inside instance initializer
In anonymous f()
*///:~
工厂方法:
interface Game { boolean move(); }
interface GameFactory { Game getGame(); }
class Checkers implements Game {
private Checkers() {}
private int moves = 0;
private static final int MOVES = 3;
public boolean move() {
print("Checkers move " + moves);
return ++moves != MOVES;
}
public static GameFactory factory = new GameFactory() {
public Game getGame() { return new Checkers(); }
};
}
public class Games {
public static void playGame(GameFactory factory) {
Game s = factory.getGame();
while(s.move())
;
}
public static void main(String[] args) {
playGame(Checkers.factory);
}
} /* Output:
Checkers move 0
Checkers move 1
Checkers move 2
*///:~
八、为什么使用内部类
优点:
①多个内部类是可以继承多个类或抽象类,不管外部类是否继承该类
②解决设计问题,使得多重继承变的更加完整
interface U {
void f();
void g();
String toString();
}
class A {
U buildU() {
return new U() {
public void f() { System.out.println("f()"); }
public void g() { System.out.println("g()"); }
public String toString() { return "I'm a U"; }
};
}
}
class B {
private U[] us;
B(int i) {
us = new U[i];
}
void addU(U u, int i) {
us[i] = u;
}
void eraseU(int i) {
us[i] = null;
}
void testUs() {
for(U u : us) {
u.f();
u.g();
u.toString();
}
}
void showUs() {
for(U u : us) {
if(u != null) System.out.println(u.toString());
else System.out.println("I'm null");
}
}
}
public class Ex23 {
public static void main(String[] args) {
A a0 = new A();
A a1 = new A();
A a2 = new A();
B b = new B(3);
b.addU(a0.buildU(), 0);
b.addU(a1.buildU(), 1);
b.addU(a2.buildU(), 2);
b.showUs();
b.testUs();
b.eraseU(0);
b.eraseU(1);
b.showUs();
}
}
闭包:
内部类是一个闭包,有自己的成员作用域,也有外部类的引用,可以操作外部类的成员(包括private修饰的)。
回调:
运行时才知道调用的方法,调用后返回一些信息(比如dubbo,这样就知道是否执行成功)
interface Incrementable {
void increment();
}
// Very simple to just implement the interface:
class Callee1 implements Incrementable {
private int i = 0;
public void increment() {
i++;
print(i);
}
}
class MyIncrement {
public void increment() { print("Other operation"); }
static void f(MyIncrement mi) { mi.increment(); }
}
// If your class must implement increment() in
// some other way, you must use an inner class:
class Callee2 extends MyIncrement {
private int i = 0;
public void increment() {
super.increment();
i++;
print(i);
}
private class Closure implements Incrementable {
public void increment() {
//调用了外部类的increment方法,不然就死循环
Callee2.this.increment();
}
}
Incrementable getCallbackReference() { //回调方法返回引用
return new Closure();
}
}
class Caller {
private Incrementable callbackReference;
Caller(Incrementable cbh) { callbackReference = cbh; }
void go() { callbackReference.increment(); } //回调方法,执行操作有输出
}
public class Callbacks {
public static void main(String[] args) {
Callee1 c1 = new Callee1();
Callee2 c2 = new Callee2();
MyIncrement.f(c2);
Caller caller1 = new Caller(c1);
Caller caller2 = new Caller(c2.getCallbackReference());
caller1.go();
caller1.go();
caller2.go();
caller2.go();
}
} /* Output:
Other operation
1
1
2
Other operation
2
Other operation
3
*///:~
九、继承内部类
1、继承非静态内部类,内部类构造器必须连接外部类的引用,这样只能先初始化外部类,才能初始化内部类。
2、静态内部类则不需要初始化外部类,随着类的加载而初始化。
class WithInner {
private String s = "_I'm ok";
WithInner(){
System.out.println("已初始化外部类"+s);
}
class Inner {
Inner(){
System.out.println("已初始化内部类"+s);
}
}
static class Inner_01{
Inner_01(){
System.out.println("已初始化非私有化静态内部类");
}
}
private static class Inner_02{
Inner_02(){
System.out.println("已初始化私有化静态内部类");
}
}
}
public class InheritInner extends WithInner.Inner {//继承非静态内部类
//! InheritInner() {} // 编译失败
InheritInner(WithInner wi) {
wi.super();//外部类引用调用内部类的构造方法
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
new WithInner.Inner_01();
// new WithInner.Inner_02(); 报错,私有化不能访问
}
} /* Output:
已初始化外部类_I'm ok
已初始化内部类_I'm ok
已初始化非私有化静态内部类 *///:~
十:内部类不可被覆盖
class Egg {
private Yolk y;
protected class Yolk {
public Yolk() { print("Egg.Yolk()"); }
}
public Egg() {
print("New Egg()");
y = new Yolk();
}
}
public class BigEgg extends Egg {
public class Yolk {
public Yolk() { print("BigEgg.Yolk()"); }
}
public static void main(String[] args) {
new BigEgg();
}
} /* Output:
New Egg()
Egg.Yolk()
*///:~
如下继承内外部类提升类型调用:覆盖的内部类
class Egg2 {
protected class Yolk {
public Yolk() { print("Egg2.Yolk()"); }
public void f() { print("Egg2.Yolk.f()");}
}
private Yolk y = new Yolk();
public Egg2() { print("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() { print("BigEgg2.Yolk()"); }
public void f() { print("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()
*///:~