您的位置:首页 > 编程语言 > Java开发

《java编程思想》学习笔记——内部类五

2017-12-01 00:00 232 查看
10.5 在方法和作用域内的内部类

1、可以在一个方法里面或者任意的作用域内定义内部类。这么做有两个理由:

(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);
}

public static void mian(String[] args)
{
Parcel5 p = new Parcel5();
Destination d = p.destination("");
}
}

2、以下的内部类TrackingSlip其实和其他的类一起编译过了。然而在定义TrackingSlip的作用域之外,它是不可用的,除此之外,它和普通的类一样。

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();
}
// Can't use it here! Out of scope:
//! TrackingSlip ts = new TrackingSlip("x");
}
public void track() { internalTracking(true); }
public static void main(String[] args) {
Parcel6 p = new Parcel6();
p.track();
}
}

10.6匿名内部类

1、使用默认构造器

public class Parcel7 {
public Contents contents() {
return new Contents() {
private int i = 11;
public int value() { return i; }
};
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents();
System.out.println(c.value());
}
}

以上写法等价于以下写法:

public class Parcel7b {
class MyContents implements Contents {
private int i = 11;
public int value() { return i; }
}
public Contents contents() { return new MyContents(); }
public static void main(String[] args) {
Parcel7b p = new Parcel7b();
Contents c = p.contents();
System.out.println(c.value());
}
}

2、基类需要一个有参数的构造器

public class Wrapping {
private int i;
public Wrapping(int x) { 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) {
Parcel8 p = new Parcel8();
Wrapping w = p.wrapping(10);
}
}

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

public class Parcel9 {
public Destination destination(final String dest) {
return new Destination() {
private String label = dest;
public String readLabel() { return label; }
};
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination d = p.destination("Tasmania");
}
}

4、在匿名内部类中不可能有命名构造器(因为它根本没有名字),但通过实例初始化,就能达到为匿名内部类创建一个构造器的效果。如:

abstract class Base {
public Base(int i) {
System.out.print("Base constructor, i = " + i);
}
public abstract void f();
}

public class AnonymousConstructor {
public static Base getBase(int i) {
return new Base(i) {
{  System.out.print("Inside instance initializer"); }
public void f() {
System.out.print("In anonymous f()");
}
};
}
public static void main(String[] args) {
Base base = getBase(47);
base.f();
}
}

此例中,不要求变量i一定是final的。因为i被传递给匿名类的基类的 构造器,它并不会在匿名内部被直接使用。

10.6.1再访工厂方法
1、例一:

interface Service {
void method1();
void method2();
}

interface ServiceFactory {
Service getService();
}

class Implementation1 implements Service {
private Implementation1() {}
public void method1() {System.out.println("Implementation1 method1");}
public void method2() {System.out.println("Implementation1 method2");}
public static ServiceFactory factory =
new ServiceFactory() {
public Service getService() {
return new Implementation1();
}
};
}

class Implementation2 implements Service {
private Implementation2() {}
public void method1() {System.out.println("Implementation2 method1");}
public void method2() {System.out.println("Implementation2 method2");}
public static ServiceFactory factory =
new ServiceFactory() {
public Service getService() {
return new Implementation2();
}
};
}

public class Factories {
public static void serviceConsumer(ServiceFactory fact) {
Service s = fact.getService();
s.method1();
s.method2();
}
public static void main(String[] args) {
serviceConsumer(Implementation1.factory);
// Implementations are completely interchangeable:
serviceConsumer(Implementation2.factory);
}
}

2、例二:

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() {
System.out.println("Checkers move " + moves);
return ++moves != MOVES;
}
public static GameFactory factory = new GameFactory() {
public Game getGame() { return new Checkers(); }
};
}

class Chess implements Game {
private Chess() {}
private int moves = 0;
private static final int MOVES = 4;
public boolean move() {
System.out.println("Chess move " + moves);
return ++moves != MOVES;
}
public static GameFactory factory = new GameFactory() {
public Game getGame() { return new Chess(); }
};
}

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);
playGame(Chess.factory);
}
}

10.7嵌套类

1、如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static.这通常陈伟嵌套类。

2、普通的内部类对象隐式的保存了一个引用,指向创建它的外围类对象。然而,当内部类 是static时,情况就如下了:

(1)要创建嵌套类的对象,并不需要其外围类的对象。

(2)不能从嵌套类的对象访问非静态的外围类对象。

3、普通内部类的字段与方法,只能放在类的外部层次上,所以普通的内部类不能有static数据和static字段,也不能包含嵌套类。但是嵌套类可以包含所有这些东西:

public class Parcel11 {
private static class ParcelContents implements Contents {
private int i = 11;
public int value() { return i; }
}
protected static class ParcelDestination
implements Destination {
private String label;
private ParcelDestination(String whereTo) {
label = whereTo;
}
public String readLabel() { return label; }
// Nested classes can contain other static elements:
public static void f() {}
static int x = 10;
static class AnotherLevel {
public static void f() {}
static int x = 10;
}
}
public static Destination destination(String s) {
return new ParcelDestination(s);
}
public static Contents contents() {
return new ParcelContents();
}
public static void main(String[] args) {
Contents c = contents();
Destination d = destination("Tasmania");
}
}

10.7.1接口内部的类
1、正常情况下,不能在接口内部放置任何代码,但嵌套可以作为接口的一部分。你放的接口的任何类都自动地是public和static的。

2、如果你想要创建某些公共代码,使得它们可以被某个接口的所有实现所公用,那么使用接口内部类的嵌套类会显得很方便。

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

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

class MNA {
private 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();
}
}

10.8为什么需要内部类

1、每个内部类都能独地继承自一个(接口的)实现,所以无乱外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

2、内部类允许继承多个非接口类型。

interface A {}
interface B {}

class X implements A, B {}

class Y implements A {
B makeB() {
// Anonymous inner class:
return new B() {};
}
}

public class MultiInterfaces {
static void takesA(A a) {}
static void takesB(B b) {}
public static void main(String[] args) {
X x = new X();
Y y = new Y();
takesA(x);
takesA(y);
takesB(x);
takesB(y.makeB());
}
}

3、如果拥有的是抽象的类或具体的类,而不是接口,那就只能使用内部类才能实现多重继承。

class D {}
abstract class E {}

class Z extends D {
E makeE() { return new E() {}; }
}

public class MultiImplementation {
static void takesD(D d) {}
static void takesE(E e) {}
public static void main(String[] args) {
Z z = new Z();
takesD(z);
takesE(z.makeE());
}
}

4、如果使用内部类,还可以获得其他一些特性

(1)内部类可以使用多个实例,每个实例都有自己的状态信息,并且与其外围类对象的信息相互独立。

(2)在单个外围类中,都可以让多个内部类以不同的方式实现同一个接口,或继承同一个类。

(3)创建内部类对象的时刻并不依赖于外围类对象的创建。

(4)内部类并没有令人迷惑的“is-a”关系,它就是一个独立的实体。

10.8.1闭包与回调

1、内部类是面向对象的闭包,因为它不仅包含外围类对象(创建内部类的作用域)的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有的成员,包括private。

interface Incrementable {
void increment();
}

// Very simple to just implement the interface:
class Callee1 implements Incrementable {
private int i = 0;
public void increment() {
i++;
System.out.println(i);
}
}

class MyIncrement {
public void increment() { System.out.println("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++;
System.out.println(i);
}

private class Closure implements Incrementable {
public void increment() {
// Specify outer-class method, otherwise
// you'd get an infinite recursion:
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
*///:~

10.8.2内部类与控制框架

1、设计模式总将变化的事物与保持不变得事物分离开,在这个模式中,模板方法是保持不变的事物,而可覆盖的方法就是变化的事物。

10.9内部类的继承

1、那个指向外围类对象的“秘密”引用必须被初始化,而在导出中不再存在可连接的默认的对象。

class WithInner {
class Inner {}
}

public class InheritInner extends WithInner.Inner {
//! InheritInner() {} // Won't compile
InheritInner(WithInner wi) {
wi.super();
}
public static void main(String[] args) {
WithInner wi = new WithInner();
InheritInner ii = new InheritInner(wi);
}
}

10.10内部类可以被覆盖吗

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

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()
*///:~

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()
*///:~

10.11局部内部类

1、唯一的理由是,我们需要一个命名的构造器,或是需要重载构造器,而匿名内部类只能用于实例初始化。所以使用局部类而不使用匿名内部类的另一个理由是,需要不只一个该内部类的对象。

interface Counter {
int next();
}

public class LocalInnerClass {
private int count = 0;
Counter getCounter(final String name) {
// A local inner class:
class LocalCounter implements Counter {
public LocalCounter() {
// Local inner class can have a constructor
System.out.println("LocalCounter()");
}
public int next() {
System.out.println(name); // Access local final
return count++;
}
}
return new LocalCounter();
}
// The same thing with an anonymous inner class:
Counter getCounter2(final String name) {
return new Counter() {
// Anonymous inner class cannot have a named
// constructor, only an instance initializer:
{
System.out.println("Counter()");
}
public int next() {
System.out.println(name); // Access local final
return count++;
}
};
}
public static void main(String[] args) {
LocalInnerClass lic = new LocalInnerClass();
Counter
c1 = lic.getCounter("Local inner "),
c2 = lic.getCounter2("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
*///:~

10.12内部类标识符
$
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: