笔记:Java内部类

内部类

使用内部类访问对象状态

内部类的对象总有一个隐式引用,指向创建它的外部类对象:

// 我们假设将外围类对象的引用称为outer(实际上是隐式的)
public class A {  // OuterClass
	private boolean yes;
	... // fields
	
	// 当在start里面构造一个内部类B对象时,
	// 编译器就会将外部类A的this引用传入内部类B的构造器
	public void start() { ...}
	
	public class B {  // InnerClass
		...  // fields
		// (隐式)自动在内部类生成一个构造器来设置outer
		public B(A xxx) {
			outer = xxx;
		}
		
		public void abc() {  // 然后调用外部类的字段
			if (outer.yes) ...;
		}
	}
}

迭代器设计模式了解内部类访问外围类成员

interface Selector {
    boolean end();
    Object current_ele();
    void next();
}

public class Sequence {
    // 初始化数组:
    private Object[] items;
    // 初始化索引
    private int index = 0;
    // 初始化数组长度
    public Sequence(int size) { items = new Object[size];}
    // 迭代按索引顺序给数组赋值
    public void add(Object x) { if (index < items.length) items[index++] = x;}

    private class SequenceSelector implements Selector {
        private int i = 0;

        public boolean end() { return i == items.length;}
        public Object current_ele() { 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 = 10; i > 0; i--) sequence.add(Integer.toString(i));
        // 构建迭代器
        Selector selector = sequence.selector();
        while (!selector.end()) {
            print(selector.current_ele() + " ");
            selector.next();  // 移到下个元素
        }
    }
}

内部类的特殊语法规则

在外围类的作用域外,可以这样引用内部类:OuterClass.InnerClass

public class Parcel2 {
    static class Contents {
        private int i = 11;
        public int value() { return i;}
    }

    static 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 = contents();
        Destination d = to(dest);
        println(d.readLabel() + c.value());
    }

    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");
    }
}

使用.this和.new

可以在内部类这样引用外部类:

public class Test {
    void f() { println("Test.f()");}

    public class Inner {
        public Test giveOuter() {
            return Test.this;  // 指向外部类
        }
    }

    public Inner giveInner() { return new Inner();}

    public static void main(String[] args) {
        Test t = new Test();
        Test.Inner i = t.giveInner();
        i.giveOuter().f();
    }
}

让其他对象创建内部类:

outerObject.new InnerClass(constructor parameters);

public class Test {
    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;}
    }
}

class Outsider {  // 独立的类
    public static void main(String[] args) {
        Test t = new Test();
        Test.Contents c = t.new Contents();  // 引用内部类
        Test.Destination d = t.new Destination("Despicable ");
        println(d.readLabel());
    }
}

我们假设在包A创建一个接口;包B创建一个包含protected内部类Inner的外围类Outer,Inner实现包A接口;在包C设计一个类继承包B的Outer,然后提供一个方法返回转型为包A接口的Inner对象。要注意的是,Inner会自动生成一个无参构造器,访问级别和Inner一样是protected,而在包C继承了包B的Outer时候,你在不同包且又不是Inner子类,在protected限制下就无法new Inner();所以要手动在Inner构造一个public Inner();

匿名内部类

如果指示创建这个类的一个对象,甚至不需要为类指定名字,这种语法是指:创建一个继承自Contents的匿名类的对象

public class Parcel7 {
    public Contents contents() {
        return new Contents() {  // 插入类的定义
            private int i = 11;
            @Override
            public int value() { return i;}
        };  // 结束
    }
}

当你的基类需要一个有参数的构造器:

class Wrapping {
    private int i;
	// 有参构造器
    public Wrapping(int x) { i = x;}
    public int value() { return i;}
}

public class Parcel8 {
    public Wrapping wrapping(int x) {  // 在这里Wrapping类似于“接口”
        return new Wrapping(x) {  // 定义继承于普通类Wrapping的子类
            public int value() { return super.value() * 47;}
        };
    }

    public static void main(String[] args) {
        Parcel8 p = new Parcel8();
        Wrapping w = p.wrapping(10);
        println(w.value());  ///: 470
    }
}

匿名内部类如果要引用外部类定义的对象,则其参数引用要final;即外部类定义的方法参数如果直接给内部类用的话则必须final,即事实最终变量

public class Parcel9 {
    public Destination destination(final String dest) {
        return new Destination() {
            private String label = dest;
            @Override
            public String readLabel() { return label;}
        };
    }
}

实例初始化,达到匿名类内部实现构造器效果:

abstract class Base { 
    public Base(int i) { println("Base constructor: " + i);}
    public abstract void f();
}

public class Parcel10 {  // 参数传给构造器而不是匿名内部类
    public static Base getBase(int i) {
        return new Base(i) {  // 实例初始化
            { println("Inside the instance initializer");}
            @Override
            public void f() { println("In anonymous f()");}
        };
    }    
}

实例初始化块:

class Skeleton {
    public Skeleton(int value, String description) { }
}

public class Parcel11 {
    public Skeleton ske(int value, String description) {
        return new Skeleton(value, description) {
            private int cost;
            {  // 无法重载,所以匿名内部类只有一个“构造器”,
               // 即所谓的实例初始化块
                cost = Math.round(value);
                if (cost > 100) println(description);
            }
        };
    }

    public static void main(String[] args) {
        Parcel11 p = new Parcel11();
        Skeleton ske = p.ske(120, "fucking cheap!");
    }
}

匿名内部类论工厂方法

工厂方法是实现代码和接口完全分离的方法之一(即解耦),生成遵循某个接口的对象的典型方法。与直接调用构造器不同,在工厂对象上调用的是创建方法,该工厂对象将生成接口的某个实现的对象。使用工厂方法,避免指定创建Service确切类型。

interface Service {  // 接口1
    void method1();
    void method2();
}

interface ServiceFactory {  // 接口2
    Service getService();
}

class Implementation implements Service {
    private Implementation() {}  // 构造器
    public void method1() { println("called Implementation method1");}
    public void method2() { println("called Implementation method2");}
    public static ServiceFactory factory = // Implementation::new
            new ServiceFactory() {  // 匿名内部工厂类,即实现了接口2
                @Override
                public Service getService() {  // 生成Service对象
                    return new Implementation();
                }
            };
}

public class FactoryDP {
    public static void serviceConsumer(ServiceFactory sf) {
        Service s = sf.getService();
        s.method1();
        s.method2();
    }
    
    public static void main(String[] args) {
        serviceConsumer(Implementation.factory);
    }
}

静态内部类(嵌套类)

静态内部类类似于其他内部类,只不过它的对象没有生成它的外围类对象的引用,所以不能从嵌套类的对象中访问非静态的外围类对象;而普通内部类不能有static数据和字段,嵌套类可以有。而且嵌套类不能有.this链接外围类对象。

public class StaticInnerClassTest {
    public static void main(String[] args) {
        var values = new double[20];
        // fill the array.
        for (int i = 0; i < values.length; i++) {
            values[i] = 100 * Math.random();
        }
        // call the static inner class
        ArrayAlg.Pair p = ArrayAlg.minmax(values);

        println("min = " + p.getFirst());
        println("max = " + p.getSecond());
    }
}

class ArrayAlg {
    public static class Pair {
        private double first;
        private double second;

        public Pair(double first, double second) {
            this.first = first;
            this.second = second;
        }

        public double getFirst() {
            return first;
        }

        public double getSecond() {
            return second;
        }
    }

    public static Pair minmax(double[] values) {
        double min = Double.POSITIVE_INFINITY;
        double max = Double.NEGATIVE_INFINITY;

        for (double v : values) {
            if (min > v) min = v;
            if (max < v) max = v;
        }
		// 将获得的值传入Pair构造器
        return new Pair(min, max);  
    }
}

接口内部类

放到接口中的任何类都自动地是public & static的,且在嵌套类中写一个测试函数main,方便

public interface ClassInFace {
    void howdy();
    class Test implements ClassInFace {
        public void howdy() { println("fuckdy!");}
    }
    class TestMain {
        public static void main(String[] args) {
            new Test().howdy();
        }
    }
}

可以这样使用接口:

interface In {
    String s();
    String f();

    class Nest {
        static void get(ClassInFace c) {
            println(c.s() + c.f());
        }
    }
}

public class ClassInFace implements In {
    public String s() { return "fuck you!";}
    public String f() { return "son of bitch!";}

    public static void main(String[] args) {
        ClassInFace c = new ClassInFace();
        In.Nest.get(c);
    }
}

你可能感兴趣的:(Java知识)