Java编程思想拾遗(9)内部类

每个内部类都能独立地继承自一个(接口的)实现,所以无论外围类是否已经继承了某个(接口的)实现,对于内部类都没有影响。

如果没有内部类提供的、可以继承多个具体的或抽象的类的能力,一些设计与编程问题就很难解决。从这个角度看,内部类使得多重继承的解决方案变得完整,接口解决了部分问题,而内部类有效地实现了多重继承。有点类似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();
        }
    }
}
复制代码

内部类的继承

当继承一个外围类并另外定义了同名内部类,其实两个内部类是毫无关联的,除非原外围类开放了内部类对象引用接口,这样外围继承类可以把对应的内部继承类传递过去。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享