• 浅谈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变量,为什么有这种要求呢?

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

  • 相关阅读:
    webmagic的使用学习
    redis在macOS上的安装及与springboot的整合使用
    Swagger-UI
    个人作业——软件工程实践总结&个人技术博客
    祝贺大野鸡喜提小黄衫一件
    软件评测(个人作业)
    结对第二次作业
    Springboot项目创建文件中相对路径问题
    二进制翻转
    欧拉降幂及广义欧拉降幂证明
  • 原文地址:https://www.cnblogs.com/zemliu/p/2755685.html
Copyright © 2020-2023  润新知