java编程思想 ch10 内部类

嵌套类(nested class)是指被定义在另一个类的内部的类。嵌套类的目的应该只是为它的外围类(enclosing class)提供服务。如果嵌套类将来可能会用于其他某个环境中,它就应该是顶层类,而不是被设计为嵌套类。嵌套类分如下四种:
静态成员类(static member class)、非静态成员类(nostatic member class)、匿名类(anonymous class)和局部类(local class)。
除了静态成员类之外,其他三种都被称为内部类(Inner Class)。静态成员类和非静态成员类称为成员类,后两个称为非成员类。

10.1 创建内部类

package Ch10;

public class Parcel2 {
    class Contents{
        private int i=11;
        public int value(){return i;}
    }
    public Contents contents(){
        return new Contents();
    }
    class Destination{
        private String label;
        Destination(String whereTo){
            label = whereTo;
        }
        String readLabel(){return label;}
    }
    public Destination to(String s){
        return new Destination(s);
    }

    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();
        //outter.inner 指明类
        Parcel2.Contents c = q.contents();
        Parcel2.Destination d = q.to("Borneo");
        System.out.println(d.readLabel());
    }
}
/*
    Tasmania
    Borneo
*/

10.2 链接到外部类

内部类拥有外部类的所有元素的访问权。

因为:
当一个外围类创建一个内部类对象时,此内部类对象必定会秘密捕获一个指向那个外围对象的引用。

内部类只能在和外部类相关联的情况下创建(除了静态成员内部类)

package Ch10;
interface Selector{
    boolean end();
    Object current();
    void next();
}

public class Sequence {
    private Object[] items;
    private int next = 0;
    public Sequence(int size){items = new Object[size];}
    public void add(Object x){
        items[next++]=x;
    }
    private class SequenceSelector implements Selector{
        private  int i=0;
        //子类方法,即使方法名一样方法签名不一样,也是完全不一样的方法,并不是覆盖。
        //方法签名包括:返回类型,方法名,参数。
        //不能写成 boolean end(int i)
        //继承或者实现不能缩小访问权限。interface默认public,所以不写public就默认是proteed,出错。
        public boolean end(){ return i==items.length;}
        public  Object current(){return items[i];}
        public  void next(){ if(i<items.length) i++;}
    }
    public Selector selector(){
        return new SequenceSelector();
    }

    public static void main(String[] args) {
        Sequence sequence = new Sequence(10);
        for(int i=0; i<10; i++){
            sequence.add(i+"");
        }
        Selector selector = sequence.selector();
        while(!selector.end()){
            System.out.print(selector.current()+" ");
            selector.next();
        }
    }
}
/*
0 1 2 3 4 5 6 7 8 9 */

10.3 使用.this and .new

outter.this 产生外部类对象的引用。

package Ch10;

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

外部了对象.new,创建内部类对象。

public class DotNew{
    public class Inner{}
    public static void main(String[] args){
        DotNew dn = new DotNew();
        DotNew.Inner dni = dn.new Inner();
    }
}

10.4

package Ch10;

import sun.security.krb5.internal.crypto.Des;

interface Destination{
    String readLabel();
}

interface Contents{
    int value();
        }

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();
       Destination d = p.destination("Tasmania");

    }
}
内部类实现接口,来隐藏细节。

10.5 在方法和作用域内的内部类

局部内部类

10.6 匿名内部类

使用外部的变量必须声明为final

10.7 嵌套内部类(不是真的内部类)

不需要内部类对象和外部类对象之间有联系,可以将内部类声明为static。嵌套类。
意味着:
1)要创建嵌套类的对象,并不需要其外围类的对象。
2)不能从嵌套类的对象中访问非静态的外围类对象。
(可以通过创建外部类对象,间接访问)

所以,普通内部类不能有static数据和字段,也不能包含嵌套内部类。就是不能static。

10.7.1 接口内部的类

放到接口内部的任何类都是static的,只是将嵌套类放于接口的命名空间,并不违反接口的规则。
package Ch10;
public interface ClassInInteface {
    void howdy();
    class Test implements ClassInInteface{
        public void howdy(){
            System.out.println("Howdy!");
        }

        public static void main(String[] args) {
            new Test().howdy();
        }
    }
}
Howdy!

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

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():
    mnabb.h();
}
}

10.8 为什么需要内部类

接口解决了多重继承的部分问题,内部类使多重继承方案完整。
每个内部类都可以独立地继承一个(接口的实现),所以不论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
如果拥有的是抽象类或者具体的类,而不是接口,只有内部类才能实现多重继承。

10.8.1 闭包和回调

闭包(closure)是一个可调用的对象,它记录了一些信息,这些信息来自于创建它的作用域。通过这个定义,可以看出内部类是面向对象的闭包。

软件模块之间总是存在着一定的接口,从调用方式上,可以把他们分为三类:同步调用、回调和异步调用。
同步调用是一种阻塞式调用,调用方要等待对方执行完毕才返回,它是一种单向调用;
回调是一种双向调用模式,也就是说,被调用方在接口被调用时也会调用对方的接口;
异步调用是一种类似消息或事件的机制,不过它的调用方向刚好相反,接口的服务在收到某种讯息或发生某种事件时,会主动通知客户方(即调用客户方的接口)。
回调和异步调用的关系非常紧密,通常我们使用回调来实现异步消息的注册,通过异步调用来实现消息的通知。同步调用是三者当中最简单的,而回调又常常是异步调用的基础,因此,下面我们着重讨论回调机制在不同软件架构中的实现。

进一步展示了外围类实现一个接口与内部类实现此接口之间的区别。

package Ch10;
import static tools.Print.print;

interface Incrementable{
    void increment();
}
// 外围类实现一个接口
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();}
}
// MyIncrement已经有了一个不同的increment()方法,
// 如果Callee2继承了MyIncrement,就不能为了Incrementable而覆盖本身的increment()方法。
// 于是只能使用内部类独立实现Incremeabele。而且创建了内部类并没有在外围类的接口添加东西
// 也没有修改外围类的接口。
class Callee2 extends MyIncrement{
    private int i =0;
    public void increment(){
        super.increment();
        i++;
        print(i);
    }
    //内部类实现此接口
    private class Closure implements Incrementable{
        public void increment(){
            Callee2.this.increment();
            System.out.println(Callee2.this);
            System.out.println(this);
        }
    }
    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();
    }
}
    other operation
1
        1
        2
        other operation
        2
        Ch10.Callee2@1540e19d
        Ch10.Callee2$Closure@677327b6
        other operation
        3
        Ch10.Callee2@1540e19d
        Ch10.Callee2$Closure@677327b6

10.8.2 内部类和控制框架

control framework。
application framework 就是被设计用于解决某类特定问题的一个类或一组类。
Thinking in Patterns(with java)
control framework 是一类特殊的应用程序框架,解决响应事件的需求。主要用来响应事件的系统,被称为事件驱动系统。

10.9 内部类的继承

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

package Ch11;
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);
    }
}

10.10 内部类可以被覆盖吗

当继承某个外围类时,这两个内部类时完全独立的两个实体,各自在自己的命名空间里。

//明确继承某个内部类
package Ch11;
import static tools.Print.print;

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:
  //先加载BigEgg2的基类,初始化其域。new folk() 语句打印
  Egg2.Yolk()
  // 再调用基类构造器,打印
  new Egg2()
  //初始化导出类域,再调用导出类构造函数,public BigEgg2(){insertYolk(new Yolk());}
  // 调用基类构造器,Egg2中Yolk构造器,打印
  Egg2.Yolk()
  // 再调用导出类中Yolk构造器,打印
  BigEgg2.Yolk()
  //将导出类Yolk向上转型,将导出类BigEgg2向上转型,Egg2 e2 = new BigEgg2();
  //    e2.g();导出类没有覆盖基类g(),调用基类g()。y.f(),调用导出类f(),打印
    BigEgg2.Yolk().f()

10.11 局部内部类

不能有访问说明符。

需要不止一个内部类对象,或者需要一个已经命名的构造器或者需要重载构造器,而匿名内部类只能用于初始化一个内部类对象。

interface Counter(){
    int next();    
}

public class local LocalInnerClass{
    private int count = 0;
    Counter getCounter(final String name){
        //外部类中被局部内部类使用的变量必须为final
        class LocalCounter implements Counter{
            public LocalCounter(){
                print("LocalCounter");
            }
            public int next(){
                priintnb(name);
                return count++;
            }
        }
        return new LocalCounter();
    }

    Counter getCounter2(final String name){
        // 看似返回了一个接口的实例,实则因为匿名内部类的特殊语法,返回的是实现了这个接口的一个实例
        return new Counter(){
            {
                print("Counter()");

            }
            public int next(){
                print(name);
                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<2; i++)
            print(c1.next());
        for(int i=0; i<2; i++){
            print(c2.next());}
    }

}
output:
    LocalCounter();
    Counter();
    Local Inner 0
    Local Inner 1
    Anonymous inner 2
    Anonymous inner 3

10.12 内部类标识符

每个类都会产生.class文件。
内部类: 外围类名字,加上”$”,加上内部类的名字。
匿名内部类,编译器产生一个数字作为其标识符。

内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两类。对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现outer.class和outer$inner.class两类。所以内部类的成员变量/方法名可以和外部类的相同。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值