浅谈Java内部类

Java的内部类可以获得其外围类的所有成员访问权限,可以很方便的对外围类进行操作。非static内部类都含有一个隐藏的外围类引用,这个引用可以通过 OuterClass.this 来获得使用。通常如果我们的一组操作需要遵循某些规范(例如接口,抽象类),就可以把这一组操作封装成一个内部类来供外部使用,而又不会脱离外围类。参见如下代码:

public interface Selector<E> {
    boolean hasNext();
    E next();
}

public class Sequence {
    private int[] seq;
    private Selector<Integer> obverseSelector;    // 正序遍历器
    private Selector<Integer> reverseSelector;    // 逆序遍历器

    public Sequence(int[] seq) {
        this.seq = seq;
        obverseSelector = new Selector<Integer>() {
            int counter = -1;
            
            @Override
            public Integer next() {
                return Sequence.this.seq[counter];
            }
            
            @Override
            public boolean hasNext() {
                return (++counter < Sequence.this.seq.length);
            }
        };
        
        reverseSelector = new Selector<Integer>() {
            int counter = Sequence.this.seq.length;
            
            @Override
            public Integer next() {
                return Sequence.this.seq[counter];
            }
            
            @Override
            public boolean hasNext() {
                return (--counter >= 0);
            }
        };
    }
    
    public Selector<Integer> getObverseSelector() {
        return obverseSelector;
    }
    
    public Selector<Integer> getReverseSelector() {
        return reverseSelector;
    }
}

public class Test {
    public static void main(String args[]) {
        int[] a = {1, 2, 3, 4, 5};
        
        Sequence sequence = new Sequence(a);
        Selector<Integer> obSelector = sequence.getObverseSelector();
        Selector<Integer> reSelector = sequence.getReverseSelector();
        while (obSelector.hasNext()) {
            System.out.print(obSelector.next() + " ");
        }
        System.out.println();
        while (reSelector.hasNext()) {
            System.out.print(reSelector.next() + " ");
        }
    }
}

这个例子通过内部类实现Selector接口,让Sequence类拥有属于自己的正序遍历器和逆序遍历器。注意匿名内部类如果需要访问外围类的成员,需要使用 Outer.this 前缀来访问。

可以想象如果不用内部类来实现遍历器会有什么不同,如果我们不用内部类来实现,就必须定义个Selector的具体类,并且将Sequence类的seq数组作为参数传进去。如果说需要将Selector作为一个共用的遍历器,可以这么做,但如果遍历器是针对Sequence类来实现的,则用内部类更为方便。

下面的代码是使用非匿名内部类实现的Selector

public class Sequence2 {
    private int[] seq;
    
    public Sequence2(int[] seq) {
        this.seq = seq;
    }
    
    public class ObverseSelecter implements Selector<Integer> {
        int counter = -1;
        
        @Override
        public Integer next() {
            return seq[counter];
        }
        
        @Override
        public boolean hasNext() {
            return (++counter < seq.length);
        }
    }
    
    public class ReverseSelecter implements Selector<Integer> {
        int counter = seq.length;
        
        @Override
        public Integer next() {
            return seq[counter];
        }
        
        @Override
        public boolean hasNext() {
            return (--counter >= 0);
        }
    }
}

public class Test {
    public static void main(String args[]) {
        int[] a = {1, 2, 3, 4, 5};
        
        Sequence2 sequence2 = new Sequence2(a);
        Selector<Integer> obSelector2 = sequence2.new ObverseSelecter();
        Selector<Integer> reSelector2 = sequence2.new ReverseSelecter();
        while (obSelector2.hasNext()) {
            System.out.print(obSelector2.next() + " ");
        }
        System.out.println();
        while (reSelector2.hasNext()) {
            System.out.print(reSelector2.next() + " ");
        }
    }
}

此处仅需要注意一下生成遍历的时候的new语法 sequence2.new ,容易看出内部类的实例其实是和外围类的实例紧密相关的,正如匿名内部类的 Outer.this 语法一样

值得一提的是局部内部类如果要使用局部变量,那么局部变量必须为final,否则编译会不通过,参见如下代码

public interface Flyable {
    void fly();
}

public class FinalTest {
    public static Flyable getFlyable(int speed) {
        final int finalSpeed = speed * 2;
        return new Flyable() {
            @Override
            public void fly() {
                System.out.println("I can fly with speed " + finalSpeed + " km/h");
            }
        };
    }
    
    public static void main(String[] args) {
        Flyable flyable = getFlyable(50);
        flyable.fly();
    }
}

可见finalSpeed为final变量,为什么有这种要求呢?

是因为局部变量的生命周期仅仅在方法执行作用域内,当方法执行完毕,局部变量自然就被销毁。但是内部类却作为一个实例被返回来了,如果说这个内部类实例以后还需要访问这个已销毁的局部变量,显然是不可以的。所以在内部类里需要访问的局部变量都会定义成常量,一起被编译进内部类里。

原文地址:https://www.cnblogs.com/zemliu/p/2755685.html