Java 内部类.md

Java 内部类

学习自

《Java编程思想》

Overview

什么是内部类?
Thinking In Java 中如此定义: 将一个类的定义放在里另一个类的定义的内部,这就是内部类。

声明一个内部类

package com.company;

public class Parcel {
    class Contents {
        private int i = 11;

        public int value() {
            return i;
        }
    }

    class Destination {
        private String label;

        Destination(String whereTo) {
            this.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) {
    Parcel p = new Parcel();
    p.ship("Tasmania");
}

上面是一个简单的内部的声明,并没有什么特殊的情况,除了将类定义在一类的声明中,这种比较奇怪的写法外。

创建内部类的对象

如果想要确切地指向一个内部类的语法是: OuterClassName.InnerClassName
根据这种个语法我们来创建内部类的对象来试试:

图片.png | left | 702x104

按照这种语法报了编译错误,显然这不是正确的方式,正确的方式如下(在外部类中暴露实例化内部类的方法):



public class Parcel {
    class Contents {
        private int i = 11;

        public int value() {
            return i;
        }
    }

    class Destination {
        private String label;

        Destination(String whereTo) {
            this.label = whereTo;
        }

        String readLabel() {
            return label;
        }
    }
    //!!注意这里,我们暴露了方法来实例化内部类
    public Destination to(String dest) {
        return new Destination(dest);
    }

    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) {
    Parcel p = new Parcel();
    Parcel.Destination destination = p.to("BeiJing");
}

我们知道了如果正确地实例化内部类,我们也应该知道为什么,直接通过 new 的方式不能实例化内部类:

这是因为,在内部类中是可以引用外部类的成员的(这个稍后会提到),如果直接实例化内部列的话,内部类,是无法访问到外部类的实例成员的,因为外部类还没有实例化。
不过,如果是想直接实例化内部类的话,也不是不可以,在后面我们会讲到那就是 静态内部类

链接到外部类

public class Sequence {
    private Object[] items;
    private int next = 0;

    public Sequence(int length) {
        items = new Object[length];
    }

    public void add(Object item) {
        if (next < items.length) {
            items[next++] = item;
        }
    }

    public SequenceSelector selector() {
        return new SequenceSelector();
    }

     class SequenceSelector {
        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 static void main(String[] args) {
    int length = 10;

    Sequence s = new Sequence(length);
    for (int i = 0; i < length; i++) {
        s.add(i);
    }

    Sequence.SequenceSelector selector = s.selector();
    while (!selector.end()) {
        System.out.print(selector.current() + ",");
        selector.next();
    }
}
//输出结构
//0,1,2,3,4,5,6,7,8,9,

上面我们,我们使用内部类完成了一个简单的迭代器模式的代码,值得注意的是,在 Selector 我们用到了items对象,值得注意的是,此对象并不是Selector本身的,而是Sequence的一个private成员。内部类可以访问器外部类的方法和字段,就像是自身拥有的似的. 通过这一特性,我们在一些特定的情况下,可以完成一些非常优雅的设计,比如说迭代器模式。

为什么内部类可以访问外部类的对象

原因是,当一个外部类的对象,创建了一个内部对象时,内部类会秘密地捕获一个执行外部类的对象的应用。我们不必去关心细节,因为编译器已经帮助我们处理好了,如此一来,为什么不能通过 new 关键字来实例化非__静态内部类__的疑惑也就迎刃而解。

.this 和 .new

.this 关键字

如果在内部类中想要获取对外部类的引用,可以通过 OuterName.this 来获取。


public class Outer {
    public void test() {

    }

    class Inner {
        public void foo() {
            //Outer.this 获取外部类的引用
            Outer.this.test();
        }
    }
}

.new 关键字

如果想要实例化一个必须要用外部类的引用才行,所以我们一开始创建内部类的实例的时候是这样创建的

public class Parcel {
    class Contents {
        private int i = 11;

        public int value() {
            return i;
        }
    }

    class Destination {
        private String label;

        Destination(String whereTo) {
            this.label = whereTo;
        }

        String readLabel() {
            return label;
        }
    }
    //!!注意这里,我们暴露了方法来实例化内部类
    public Destination to(String dest) {
        return new Destination(dest);
    }
}

上面的代码中,我们为了创建内部类的对象,我们不得不写了一个公开的方法,但是这无疑增加了我们的工作量,虽然并不多。现在有一个新的方式来创建内部类的实例---通过 .new 关键字。

Parcel parcel = new Parcel();
Parcel.Destination destination = parcel.new Destination("temp");

方法和作用域中的内部类

在我们想要创建一个类来辅助我们解决一个比较复杂得问题的时候,但是与此同时,又不希望这个类是公共的。

public static void main(String[] args) {
    class Person {
        private String name;
        private String gender;
        private int age;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public String getGender() {
            return gender;
        }

        public void setGender(String gender) {
            this.gender = gender;
        }

        public int getAge() {
            return age;
        }

        public void setAge(int age) {
            this.age = age;
        }
    }

    Person person = new Person();
    person.setName("LD");
    person.setGender("Male");
    person.setAge(20);
}

就比如说Person类这个类仅仅能够在main方法中使用,在其他的地方是不能够被访问的。

匿名内部类

因为我主要在做一些Android的开发,在做一些小的Demo或者没有为了实现某些接口仅仅写较短的几句代码,就完全没有必要再新建一个类来解决了,我会使用匿名内部类来解决。

Button openBtn = this.findViewById(R.id.openBtn);
openBtn.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        
    }
});

嵌套类

上面说到,为了访问外部类的种类,内部类会捕获外部类的引用。当时已经确定内部类不会调用外部类的对象的时候,可以将其声明为 嵌套类 。其有以下特点:

  • 嵌套类并不需要其外围类的对象
  • 不能从嵌套类中访问费静态类型的成员
  • 因为没有对外部类的引用,可以直接通过 new 关键字实例化
public class Parcel {
    public static class Destination {
        private String label;

        public Destination(String whereTo) {
            label = whereTo;
        }

        public void readLabel() {
            System.out.print(label);
        }
    }
}
//因为Destination是嵌套类所以能够直接被实例化
public static void main(String[] args) {
    Parcel.Destination destination = new Parcel.Destination("Beijing");
    destination.readLabel();
}
posted @ 2018-07-04 21:16  鲁迅认识的那只猹  阅读(456)  评论(0编辑  收藏  举报