每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。
如果没有内部类提供的、可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整,接口解决了部分问题,而内部类有效地实现了多重继承。有点类似JDK动态代理和CGLIB的关系。
典型内部类
当生成一个内部类对象时,此对象与制造它的外围对象之间就有了一种联系,所以它能访问其外围对象的所有成员,而不需要任何特殊条件,此外内部类还拥有其外围类所有元素的访问权。
鉴于这个特性可以在多个内部类之间共享外围类元素,而多个内部类可能继承或实现同一抽象接口,对于外部客户端程序这些内部类看似游离独立,实则互相关联。
当某个外围类的对象创建了一个内部类对象时,此内部类对象必定会秘密地捕获一个指向那个外围类对象的引用,然后在你访问外围类成员时,就是用那个引用来选择外围类成员。编译器会帮你处理所有的细节,内部类的对象只能在与其外围类的对象相关联的情况下才能被创建(不允许这个内部类是static类,这种就称为嵌套类)。
普通内部类不能有static数据和static字段(不能有任何独立于外围类对象的成分),也不能包含嵌套类。
private内部类给类的设计者提供了一种途径,组合向上转型,可以完全阻止任何依赖于类型的编码,并且完全隐藏了实现的细节。
interface Selector {
boolean ene();
Object current();
void next();
}
public class Sequence {
private Object[] item;
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.println(selector.current() + " ");
selector.next();
}
}
}
复制代码
如果不是外围类内部方法创建内部类,需要使用OuterClassObject.new InnerClassName()语法。
public class DotNew {
public class Inner {}
public static void main(String[] args) {
DotNew dn = new DotNew();
DotNew.Inner dni = db.new Inner();
}
}
复制代码
如果是外部程序想通过内部类对象返回外围类对象引用,可以用OuterClassName.this语法。
public class DotThis {
void 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();
}
}
复制代码
局部内部类
在方法的作用域内(而不是在其他类的作用域内)创建一个完整的类,这被称作局部内部类。其实就是把class定义放置在方法内部,甚至可以定义在if等作用域内。
使用局部内部类而不是匿名内部类的场景:
- 需要一个已命名的构造器,或者需要重载构造器。
- 需要不止一个该内部类的对象。
匿名内部类
类似局部内部类,但这个类没有自己的类名,直接向上转型并返回。
public class Parcel7 {
public Contens contents() {
return new Contents() {
private int i = 1l;
public int value() {
return i;
}
}; // Semicolon required in this case;
}
public static void main(String[] args) {
Parcel7 p = new Parcel7();
Contents c = p.contents():
}
}
复制代码
如果定义一个匿名的内部类,并且希望它使用一个在其外部定义的对象,那么编译器会要求其参数引用是final的。
局部变量的生命周期与局部内部类对象的生命周期不一致,将final局部变量复制一份,复制品直接作为局部内部类对象的数据成员,final实现了变量在内外部的统一。
public class Parcel9 {
public Destination destination(final Stirng dest) {
return new Destination() {
privatr String label = dest;
public String readLabel() {
return label;
}
}
}
public static void main(String[] args) {
Parcel9 p = new Parcel9();
Destination p = p.destination("Tss");
}
}
复制代码
嵌套类
如果不需要内部类对象与其外围类对象之间有联系,那么可以将内部类声明为static,这被称为嵌套类。
- 要创建嵌套类的对象,并不需要其外围类的对象。
- 不能从嵌套类的对象中访问非静态的外围类对象(没有对象引用无法访问)。
正常情况下,不能在接口内部放置任何代码,但嵌套类可以作为接口的一部分,你放到接口中的任何类都自动地是public和static的,因为类是static的,只是将嵌套类置于接口的命名空间内,这并不违反接口的规则,你甚至可以在内部类中实现其外围接口。
如果你想要创建某些公共代码,使得它们可以被某个接口的所有不同实现所共用,那么使用接口内部的嵌套类会显得很方便。
public interface ClassInInterface {
void howdy();
class Test implements ClassInInterface {
public void howdy() {
System.out.println("Howdy!");
}
public static void main(String[] args) {
new Test().howdy();
}
}
}
复制代码
内部类的继承
当继承一个外围类并另外定义了同名内部类,其实两个内部类是毫无关联的,除非原外围类开放了内部类对象引用接口,这样外围继承类可以把对应的内部继承类传递过去。