Java编程思想---第十章 内部类(上)

第十章  内部类(上)

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

  内部类是一种非常有用的特性,它允许你把一些逻辑相关的类组织在一起,并控制位于内部的类的可视性,但内部类与组合是完全不同的概念。

10.1 创建内部类

  创建内部类的方法就是,把类的定义置于外围类的里面:

public class Parcel1 {
    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 void ship(String dest) {
        Contents c = new Contents();
        Destination d = new Destination(dest);
        System.out.println(d.readLabel());
    }
    public static void main(String[] args) {
        Parcel1 p = new Parcel1();
        p.ship("Tasmania");
    }
}

  当我们在ship()方法里使用内部类的时候与使用普通类没什么不同,实际的区别就只是内部类的名字嵌套在Parcel1中。

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 = new Contents();
        Destination d = new Destination(dest);
        System.out.println(d.readLabel());
    }
    public static void main(String[] args) {
        Parcel2 p = new Parcel2();
        p.ship("Tasmania");
        Parcel2 q = new Parcel2();
        Parcel2.Contents c = q.contents();
        Parcel2.Destination d = q.to("Borneo");
    }
}

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

10.2 链接到外部类

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

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) {
        if(next < items.length)
            items[next++] = x;
    }
    private class SequenceSelector implements Selector {
        private int i = 0;
        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(Integer.toString(i));
        Selector selector = sequence.selector();
        while (!selector.end()) {
            System.out.print(selector.current() + " ");
            selector.next();
        }
    }
}

  Sequence只是一个固定大小的Object数组,以类的形式包装了起来,可以调用add()在序列末增加新的Object,要获取Sequence中的每一个对象,可以使用Selector接口。注意方法end()、current()、next()都用到了objects,这是一个引用,它并不是SequenceSelector的一部分,而是外围类中的一个private字段,然而内部类可以访问其外围类的方法和字段,这带来了极大的方便。

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 dt = new DotThis();
        DotThis.Inner dti = dt.inner();
        dti.outer().f();
    }
}

  有时候你可能想要告知某些其他对象去创建其某个内部类的对象,你必须在new表达式中提供对其他外部类对象的引用,这时需要使用.new语法:

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

  要想直接创建内部类的对象,你不能按照你想象的方式去引用外部类的名字DotNew,而是必须使用外部类的对象来创建该内部类对象,就像在上面看到的那样,这就解决了内部类名字作用域的问题。

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

  下面我们把.new应用到Parcel:

public class Parcel3 {
    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 static void main(String[] args) {
        Parcel3 p = new Parcel3();
        Parcel3.Contents c = p.new Contents();
        Parcel3.Destination d = p.new Destination("Tasmania");
    }
}

10.4 内部类与向上转型

  当将内部类向上转型为基类,尤其是转型为一个接口的时候,内部类就有了用武之地,这是因为内部类——某个接口的实现——能够完全不可见并且不可用,所得到的知识指向基类或接口的引用,所以能够很方便地隐藏实现细节。

  当取得了一个指向基类或接口的引用时,甚至可能无法找出它确切的类型:

public 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 在方法和作用域内的内部类

  通常如果读写的代码包含了内部类,那么他们都是平凡的内部类,简单而且容易理解,然而内部类的语法覆盖了大量其他的难以理解的技术,例如,可以在一个方法里面或者在任意作用域内定义内部类,这么做有两个理由:

  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 main(String[] args) {
        Parcel5 p = new Parcel5();
        Destination d = p.destination("Tasmania");
    }
}

  PDestination类是destination()方法的一部分,而不是Parcel5的一部分,所以在destination之外不能访问PDestination。你可以在同一子目录下的任意类对某个内部类使用类标识符PDestination,这并不会有命名冲突。

  下面的例子展示如何在任意的作用域中嵌入一个内部类:

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();
        }
    }
    public void track() {
        internalTracking(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;
            public int value() {
                return i;
            }
        }
    }
    public static void main(String[] args) {
        Parcel7 p = new Parcel7();
        Contents c = p.contents();
    }
}

  contents方法将返回值的生成与表示这个返回值的类的定义结合在一起,除此之外这个类是匿名的,没有名字。这种语法指的是创建一个继承自Contents的匿名类的对象,通过new表达式返回的引用被自动向上转型为对Contents的引用。匿名内部类与正规的继承相比有些受限,因为匿名内部类既可以扩展类,也可以实现接口,但是不能二者兼备,而如果是实现接口也只能实现一个接口。

10.6.1 再访工厂方法

  优化工厂方法,此处略。

  优先使用类不是接口,如果你设计中需要某个接口,你必须了解他,否则不到万不得已不要将其放在你的设计中。

原文地址:https://www.cnblogs.com/parable/p/11488003.html

时间: 2024-10-09 18:49:06

Java编程思想---第十章 内部类(上)的相关文章

Java编程思想---第十章 内部类(下)

第十章 内部类(下) 10.9 内部类的继承 因为内部类的构造器必须连接到指向其外围类对象的引用,所以在继承内部类的时候事情会变得有些复杂,问题在于那个指向外围类对象的引用必须被初始化,而在导出类中不再存在可连接的默认对象,要解决这个问题,必须使用特殊的语法来明确说清他们之间的关联: class WithInner { class Inner {} } public class InheritInner extends WithInner.Inner { InheritInner(WithInn

【Java编程思想】10.内部类

将一个类的定义放在另一个类的定义内部,这就是内部类. 10.1 创建内部类 内部类的名字是嵌套在外部类里面的 外部类可以有方法,返回一个指向内部类的调用.(外部类中可以调用内部类) 如果在外部类中,希望能在除了静态方法之外的任意位置创建某个内部类对象,那么可以向下面这样指明对象类型. OuterClassName.InnerClassName x = new InnerClassName(); 10.2 链接到外部类 在创建了一个内部类的对象后,内部类与制造它的外围对象(enclosing ob

Java编程思想(前十章)

Java编程思想 有C++编程基础的条件下, 前10章可以快速过一下,都是基本语法,不需要花太多时间. 着重中后段的一些章节,类型信息.泛型.容器.IO.并发等. 中文翻译版 阅读地址 对于一个架构师而言,掌握各种语言的优势并可以运用到系统中,由此简化系统的开发,是其架构生涯的第一步. 每一个程序员都不能固步自封,要多接触新的行业,新的技术领域,突破自我. 对象入门 类的继承一般使用'统一标记法'(UML图)来画继承的图. 在面向对象的程序中, 通常要用到上溯造型(向上转型)的技术, 需要动态绑

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

Java编程思想笔记(内部类)

      1.创建内部类       2.链接到外部类:当生成一个内部类的对象时,此对象与制造它的外围对象之间就有了一种联系,所以它能访问其外围对象的所有成员,而不需要任何特殊条件.   3.使用.this()与.new():1.如果你需要生成对外部类对象的引用,可以使用外部类的名字后面紧跟圆点和this.2.DotNew dn = new DotNew(); DotNew.Inner dni = dn.new Inner(); //要想直接创建内部类的对象,你不能按照你想象的方式,去引用外部

Java编程思想(五) —— 多态(上)

上一章,Java编程思想(四) -- 复用类里面讲到了向上转型,感觉和多态放在一起写更好. 多态,polymorphism.一个重要的特性,篇幅太长了,分上下两篇写. (1)向上转型 class TV{ public static void show(TV tv){ System.out.println("TV"); } } public class LeTV extends TV{ public static void main(String[] args) { LeTV letv

《On Java 8》中文版,又名《Java 编程思想》中文第五版

来源:LingCoder/OnJava8 主译: LingCoder 参译: LortSir 校对:nickChenyx E-mail: [email protected] 本书原作者为 [美] Bruce Eckel,即(Thinking in Java 4th Edition,2006)的作者. 本书是事实上的 Thinking in Java 5th Edition(On Java 8,2017). Thinking in Java 4th Edition 基于 JAVA 5 版本:On

Java编程思想重点笔记(Java开发必看)

Java编程思想,Java学习必读经典,不管是初学者还是大牛都值得一读,这里总结书中的重点知识,这些知识不仅经常出现在各大知名公司的笔试面 试过程中,而且在大型项目开发中也是常用的知识,既有简单的概念理解题(比如is-a关系和has-a关系的区别),也有深入的涉及RTTI和JVM底层 反编译知识. 1. Java中的多态性理解(注意与C++区分) Java中除了static方法和final方法(private方法本质上属于final方法,因为不能被子类访问)之外,其它所有的方法都是动态绑定,这意

70.JAVA编程思想——Web应用

70.JAVA编程思想--Web应用 创建一个应用,令其在真实的Web 环境中运行,它将把Java 的优势表现得淋漓尽致.这个应用的一部分是在Web 服务器上运行的一个Java 程序,另一部分则是一个"程序片"或"小应用程序"(Applet),从服务器下载至浏览器(即"客户").这个程序片从用户那里收集信息,并将其传回Web 服务器上运行的应用程序.程序的任务非常简单:程序片会询问用户的E-mail 地址,并在验证这个地址合格后(没有包含空格,而