Java编程思想之十 内部类

Posted tan-sir

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java编程思想之十 内部类相关的知识,希望对你有一定的参考价值。

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

10.1 创建内部类

创建内部类就是把类的定义置于外部类里面。

public class Parcell 
    class contents
        int i=0;
        public void GetI()
            System.out.println("contents"+i);
            i++;
        

    
    class Destintion
        private String label;
        public void GetI(String s)
            System.out.println(s);
        
    
    public contents GetontentsC()
        return new contents();
    
    public void ship(String dest)
        contents c=new contents();
        c.GetI();
        Destintion d=new Destintion();
        d.GetI(dest);
    

    public static void main(String[] args)
        Parcell p=new Parcell();
        p.ship("dwdwdw");
        Parcell.contents c=p.GetontentsC();
        c.GetI();
    

如果想从外部类的非静态方法之外的任意位置创建某个内部类的对象,那么必须具体的指明这个对象的类型:OuterClassName.InnerClassName.

10.2 链接到外部类

当生成一个内部类对象时,此对象和制造它的外围对象之间有一种联系,所有,它能访问它外围对象的所有成员,而不需要特殊条件。

public class Sequence 
    private Object[] items;
    private int next=0;
    public Sequence(int size)
        items=new Object[size];
    
    public void add(Object x)
        if (next<items.length)
            items[next++]=x;
    
    public class SequenceSelector implements Selector
private int i=0;
        @Override
        public boolean end() 
            if (i==items.length)
                return false;
            return true;
        

        @Override
        public Object current() 
            return items[i];
        

        @Override
        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(Integer.toString(i));
        
        Selector s=sequence.selector();
        while (s.end())
          System.out.print(s.current());
            s.next();
        
    

interface Selector
    boolean end();
    Object current();
    void next();

当外围类对象创建一个内部类对象时,此内部类会秘密捕获一个指向外围类对象的引用,然后,在你访问此外围类成员时,就用那个引用来选择外围类成员

10.3 使用.this与.new

如果需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟.this,这样产生的引用自动具有正确的类型。

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 d=new DotThis();
        DotThis.Inner in=d.inner();
        in.outer().f();
    

创建某个内部类对象时,需要使用.new。

public class Parcel3 
    class Contents
        private int i=11;
        public int value()return i;
    
    public class Destination
        private String label;
        Destination(String whereTo)
            label=whereTo;
        
        String readLabel()
            return label;
        
    
    public static void main(String[] args)
        Parcel3 p=new Parcel3();
        Parcel3.Contents c=p.new Contents();
        Parcel3.Destination d=p.new Destination("123");
        System.out.println(d.label);
    

10.4 内部类与向上转型

当将内部类向上转型为基类,尤其是一个接口的时候,内部类就很有用了。因为内部类的某个接口的实现是可以完全不可见并且不可用的。

public class TestParcel 
    public static void main(String[] args) 
        Parcel4 p = new Parcel4();
        Contents c = p.contents();
        Destination d = p.destination("tasmaina");
    


interface Destination 
    String readLabel();


interface Contents 
    int value();


class Parcel4 
    private class PContents implements Contents 
        private int i = 11;

        @Override
        public int value() 
            return i;
        
    

    protected class PDestionation implements Destination 
        private String label;

        private PDestionation(String whereTo) 
            label = whereTo;
        

        @Override
        public String readLabel() 
            return null;
        
    

    public Contents contents() 
        return new PContents();
    

    public Destination destination(String s)
        return new PDestionation(s);
    

PDestination是protected,所以只要Parce4及其子类,还有与Parce4同一个包中的类能访问PDestination。
private内部类给类设计者提供了一条途径,通过这种方式,可以完全阻止任何依赖于类型的编码,并且完全隐藏实现细节。

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

在方法的作用域内,创建一个完整的类

public class Parcel5 
    public Destination destination(String s)
        class PDestination implements Destination
            private String label;
            private PDestination(String whereTo)
                System.out.println(whereTo);
                label=whereTo;
            
            @Override
            public String readLabel() 
                return label;
            
        
        return new PDestination(s);
    
    public static void main(String[] args)
        Parcel5 p=new Parcel5();
        Destination d=p.destination("sa");
    

在任意的作用域嵌入一个内部类

public class Parcel6 
    private void internalTracking1(boolean b) 
        if (b) 
            class TrackingSlip 
                private String id;

                TrackingSlip(String s) 
                    id = s;
                    System.out.println(id);
                

                String getSlip() 
                    return id;
                
            
            TrackingSlip trac=new TrackingSlip("asd");
           // TrackingSlip ts = new TrackingSlip("slip");
            //String s = ts.getSlip();
        

    public void track() 
        internalTracking1(true);
    
    public static void main(String[] args)
        Parcel6 p=new Parcel6();
        p.track();
    

TrackingSlip类被嵌入在if语句的作用域内,这不是说该类创建是有条件的,它其实与别的类一起编译过。但是在TrackingSlip的作用域之外,它是不可用的。

10.6 匿名内部类

public class Parcel7 
    public Contents contents()
        return new Contents() 
            private  int i=11;
            @Override
            public int value() 
                System.out.println(i);
                return i;
            
        ;
    
    public static void main(String[] args)
        Parcel7 p7=new Parcel7();
        p7.contents().value();
    

contents()方法将返回值的生成与表示这个返回值的类的定义结合在一起。
通过new表达式返回的引用被自动向上转型。

public class Parcel7b 
    class ABContents implements Contents
        private  int i=11;
        @Override
        public int value() 
            System.out.println(i);
            return i;
        
    
    public Contents contents()
        return new ABContents();
    
    public static void main(String[] args)
        Parcel7b b=new Parcel7b();
        Contents c=b.contents();
        c.value();
    

下面的基类需要一个有参数的构造器

public class Parcel8 
    public Wrapping wrapping(int i)
        return new Wrapping(i)
        ;
    
    public static void main(String[] args)
        Parcel8 p8=new Parcel8();
        Wrapping wr=p8.wrapping(7);
    

class Wrapping
    public Wrapping(int i)
        System.out.println(i);
    

Wrapping只是一个具有具体实现的普通类,但它还是被其导出类当作公共"接口"来使用。
在匿名内部类尾部的分号,不是用来标记内部类结束的,它标记的是表达式的结束,只不过这个表达式正好表示匿名类。
如果定义一个匿名内部类,并希望它使用一个外部定义的对象,那么就要求参数引用是final的。

public class Parcel9 
    public Destination wrapping(final int i)
        return new Destination()
            private int label=i;
            @Override
            public String readLabel() 

                System.out.println(label);
                return "i";
            
        ;
    
    public static void main(String[] args)
        Parcel9 p9=new Parcel9();
        Destination wr=p9.wrapping(4);
        wr.readLabel();
    

在匿名类中不可能有命令构造器,但通过实例初始化,就能够达到为匿名内部类创建一个构造器的效果。

public class Parcel8 
    public Wrapping wrapping(int i)
        return new Wrapping(i)
            System.out.println(i+1);
        ;
    
    public static void main(String[] args)
        Parcel8 p8=new Parcel8();
        Wrapping wr=p8.wrapping(7);
    

class Wrapping
    public Wrapping(int i)
        System.out.println(i);
    

匿名内部类与正规的继承相比有些受限,因为匿名类既可以扩展类,也可以是实现接口,但不能两者兼备,如果是实现接口,也只能实现一个。

10.6.1 再访工厂模式

优先使用类而不是接口。如果设计里面需要某个接口,那么必须了解它。

10.7 嵌套类

如果不需要内部类对象与其外围对象有联系,可以使用内部类声明为static。这称为嵌套类。
普通的内部类对象隐试地保存了一个引用,指向创建它的外围类对象。当内部类是static的时,就不是这样的了。
嵌套类意味着:

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

普通内部类的字段与方法,只能放在类的外部层次上,所有普通的内部类不能有static数据和static字段,也不能包含嵌套类。

public class Parcel11 
    private static class parceContents implements Contents

        @Override
        public int value() 
            return 0;
        
    
    public static Contents contents()
        return new parceContents();
    
    public static void main()
        Contents c=contents();
    

在一个普通的内部类中,通过一个特殊的this引用可以链接到其外围类对象,嵌套类就没有这个特殊的this引用,这使得它类似一个static方法。

10.7.1 接口内部的类

正常情况下,不能再接口中放置任何代码,但嵌套类可以作为接口的一部分,放到接口中的任何类都自动是public和static的。

public interface ClassInInterface 
    void howdy();
    class Test implements ClassInInterface

        @Override
        public void howdy() 
            System.out.println("howdy");
        

        public static void main(String[] args)
            new Test().howdy();
        
    

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

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

一个内部类被嵌套多少层,它都能透明的访问所有它所嵌入的外围类对象。

class MNA 
    private void f()System.out.println("f");
    class A
        private void g()System.out.println("g");
        public class B
            void h()
                g();
                f();
            
        
    

public class Multi
    public static void main(String[] args)
        MNA mna=new MNA();
        MNA.A maa=mna.new A();
        MNA.A.B maab=maa.new B();
        maab.h();
    

.new 语法能参数正确的作用域,所有不必再调用构造器时限定类名。

为什么需要内部类

内部类继承自某个类或实现某个接口,内部类的代码操作创建它外围类的对象,所有可以认为内部类提供了某种进入外围类的接口。
每个内部类都能独立地继承自一个实现,所有无论外围类是否已经继承了某个实现,对于内部类都没有影响。
在一个类中,以某种形式实现两个接口,有两种选择:使用单一类,或者使用内部类。
但是对于抽象类或者具体的类,就只能使用内部类实现多重继承了。

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

class D 

abstract class E 

class Z extends D 
    E makeE() 
        return new E() 
        ;
    
10.8.1 闭包与回调

闭包是一个可调用对象,它记录了一些信息,这些信息来自于创建它的作用域,通过这个定义,可以看出内部类是面向对象的闭包,因为它不仅包含外围类对象的信息,还自动拥有一个指向此外围类对象的引用,在此作用域内,内部类有权操作所有成员,包括private成员。
通过回调,对象能够携带一些信息,这些信息允许它在稍后某个时刻调用初始的对象。
通过内部类实现回调:

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

interface Incrementable
    void increment();

class Callee1 implements Incrementable
private int i=0;
    @Override
    public void increment() 
        i++;
        System.out.println(i);
    


class MyIncrement
    public static int i=1;
    public void increment()
        System.out.println("Other operation");
    
    public static void f(MyIncrement mi)
        mi.increment();
    

class Callee2 extends MyIncrement
    private int i=0;
    public void increment()
        super.increment();
        i++;
        System.out.println(i);
    
    private class Closure implements Incrementable
        @Override
        public void increment() 
            Callee2.this.increment();
        
    
    Incrementable getCallbackReference()
        return new Closure();
    

class Caller
    private Incrementable callbackReference;
    Caller(Incrementable cbh)
        callbackReference=cbh;
    
    void go()callbackReference.increment();
10.8.2 内部类与控制框架

应用程序框架就是被设计用以解决某类特定问题的一个类或一组类。要应用某个应用程序框架,通常是继承一个或多个类,并覆盖某些方法。在覆盖后的方法中,编写代码定制应用程序框架提供的通用解决方案,以解决特定的问题。
控制框架是一类特殊的应用程序框架,它用来解决响应事件的需求。主要用来响应事件的系统被称为事件驱动系统
首先,接口描述了要控制的事件:

public abstract class Event 
    private long eventTime;
    protected final long delayTime;
    public Event(long delayTime)
        this.delayTime=delayTime;
        start();
    
    public void start()
        eventTime=System.nanoTime()+delayTime;
    
    public boolean ready()
        return System.nanoTime()>=eventTime;
    
    public abstract void action();

触发事件的实际控制框架:

import java.util.ArrayList;
import java.util.List;

public class Controller 
    private List<Event> eventList=new ArrayList<Event>();
    public void addEvent(Event c)
        eventList.add(c);
    
    public void run()
        while (eventList.size()>0)
            for (Event e:new ArrayList<Event>(eventList)) 
                if (e.ready())
                    System.out.println(e);
                    e.action();
                    eventList.remove(e);
                
            
        
    

在现在的设计中,我们还不知道Event到底做了什么。但是这个设计关键就是使变化的事物与不变的事物相互分离。而这就是内部类要做的事情。
内部类允许:

  • 控制框架的完整实现是由单个的类创建,从而使得实现的细节被封装了起来。内部类用来表示解决问题所必须的各种不同的action()。
  • 内部类能够很容易的访问外围类的任意成员,所以可以避免这种实现变得很笨拙。
public class GreenhouseControls extends Controller 
    private boolean light = false;

    public class LightOn extends Event 
        public LightOn(long delayTime) 
            super(delayTime);
        

        @Override
        public void action() 
            light = true;
        

        public String toString() 
            return "Light is on";
        
    

    public class LightOff extends Event 
        public LightOff(long delayTime) 
            super(delayTime);
        

        @Override
        public void action() 
            light = false;
        

        public String toString() 
            return "Light is off";
        
    

    public class Restart extends Event 
        private Event[] eventList;

        public Restart(long delayTime, Event[] eventList) 
            super(delayTime);
            this.eventList = eventList;
            for (Event e : eventList)
                addEvent(e);
        

        public void action() 
            for (Event e : eventList) 
                e.start();
                addEvent(e);
            
            start();
            addEvent(this);
        

        public String toString() 
            return "Restarting system";
        
    

    public static void main(String[] args)
        GreenhouseControls gc=new GreenhouseControls();
        Event[] eventList=
               gc.new LightOn(20000),
               gc.new LightOff(40000)
    ;
        //gc.addEvent(gc.new LightOn(20000));
        //gc.addEvent(gc.new LightOff(40000));

        gc.addEvent(gc.new Restart(200000,eventList));
        gc.run();
    

10.9 内部类的继承

内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候,那个指向外围类对象的秘密的引用必须被初始化,而在导出类中,不再存在可连接的默认对象。

public class InheritInner extends WithInner.Inner
    InheritInner(WithInner wi)//必须指向一个外围对象的引用
        wi.super();
    
    public static void main(String[] args)
        WithInner wi=new WithInner();
        InheritInner i=new InheritInner(wi);
    

class WithInner
    class Inner

10.10 内部类可以被覆盖吗

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

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


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

技术图片

10.11 局部内部类

public class LocalInnerClass

    private int count=0;
    Counter getCounter(final String name)
    
        class LocalCounter implements Counter
        
            public LocalCounter()
            
                System.out.println("LocalCounter()");
            
            @Override
            public int next()
            
                System.out.print(name);
                return count++;
            
        
        return new LocalCounter();
    
    Counter getCounter2(final String name)
    
        return new Counter()
        
            
                System.out.println("Counter()");
            
            @Override
            public int next()
            
                System.out.print(name);
                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());
    

interface Counter

    int next();

以上是关于Java编程思想之十 内部类的主要内容,如果未能解决你的问题,请参考以下文章

Java编程思想之十二 通过异常处理错误

Java编程思想 -- 内部类

Java编程思想10.内部类

《Java编程思想》笔记 第十章 内部类

《Java编程思想》第十章 内部类

java 编程思想笔记——内部类