秒懂Java

[Java菜鸟系列] 内部类与lambda表达式

2018-12-31  本文已影响1人  d61f25068828

# [Java菜鸟系列] 内部类与lambda表达式
J004-[Java菜鸟系列] 内部类

老湿:在上一节我们说到,我们需要让计算机知道,事件发生的时候该调用什么方法所以,我们需要建立一个类,签署一个协议,之后我们把这个对象传递给相应的函数....

菜鸟:好了,你不要讲了!啰里啰嗦的,还有一天就9102年了好不好,他妈的这么麻烦,你到底有没有本事啊,工资还想不想要了?

老湿:(⊙o⊙)… 息怒,息怒,同学你好性急啊,今天我们就要讲到这个事情,这份工资我是有信心拿的到的,看我的,瞧好吧。

老湿

两个尴尬:繁琐和危险

的确,很多时候,我们只需要用一次方法就够了,又要声明类,又要实例化,写半天只用一次,实在是没什么必要。

还有个问题,我们要向方法中传数据,如果方法就在类中,那么完全可以设数据为private;但是现在需要新建一个类,所以为了让它读取数据,必须要搞getter,既麻烦又不安全。

案例计时器:我们需要让计算机每隔一定的时间报时和响铃一次。

第一个情况是简化版,不使用Timer,第二个情况是正常版本,使用Timer回调。

示例-J004-1:TalkingClockSimple
//如果我们不需要使用回调,那么beep就可以是private的。
public class TalkingClockSimple {
    public static void main(String[] args) {
        new TalkingClockSimple(true).start();
    }

    private boolean beep;

    public TalkingClockSimple( boolean beep) {
        this.beep = beep;
    }

    public void start() {
        System.out.println("At the tone,the time is " + new Date());
        if (TalkingClockSimple.this.beep) Toolkit.getDefaultToolkit().beep();
    }
}
示例-J004-2:TalkingClockExterior
//如果我们使用回调,那么动作就在外部的类中,为了让其能获取beep,我们必须搞一个getter。而且整个过程都非常繁琐。

public class TalkingClockExterior {
    public static void main(String[] args) {
        new TalkingClockExterior(1000, true).start();
    }
    private int interval;
    private boolean beep;
//为了让方法得到数据,我们必须要开放一个getter
    public boolean isBeep() {
        return beep;
    }

    public TalkingClockExterior(int interval, boolean beep) {
        this.interval = interval;
        this.beep = beep;
    }
public void  start() {

    TimePrinterExterior timeprinter = new TimePrinterExterior(this);
    new Timer(interval,timeprinter).start();
    JOptionPane.showMessageDialog(null, "Quit program?");
    System.exit(0);
}

}
//这个类我们明明只需要用一次,但是它现在在在包内都是可见的,完全没这个必要。我是没办法的事情,因为class的可见性就只有两种,要不就是默认的包内,要不就是public。
 class TimePrinterExterior implements ActionListener {

     public TimePrinterExterior(TalkingClockExterior talkingclock) {
         this.talkingclock = talkingclock;
     }

     @Override

    public void actionPerformed(ActionEvent e) {

        System.out.println("At the tone,the time is " + new Date());

        if (talkingclock.isBeep()) Toolkit.getDefaultToolkit().beep();

    }
     TalkingClockExterior talkingclock;

}

最大的问题是安全-内部类

俗话说得好,留得青山在,不怕没柴烧,只要小命在,什么都好说。

安全这个问题永远是最重要的。

前面我们提到了之前方案的两个重要缺点,一个是"麻烦",另一个是"不安全",本着"要事第一"的原则,我们首先解决它的安全性问题。

那我们究竟该如何解决呢?

在移动通信领域,高通垄断了高端基带,所以其他公司主要生产手机就必须交钱。

高通基带谁也可以买,是公共的,厂商很难在这个层面搞创术,就算搞了,也很容易被仿制。

不仅这样,还交大量的专利费,这又被称为"高通税",更可恶的是,专利费不是按照芯片来收,而是按照整机的售价抽取一定比例。

苹果的老大库克终于坐不住了,他娘的,老资苹果公司还要看你高通脸色,"老资不跟你玩了!"。

库克

苹果一气之下放弃了高通的方案,拉上了同样失意的小兄弟英特尔,一起搞了一个新的基带,自研自产自销。

由于是自研技术,当然不用再交专利费,再添加什么其他功能也非常的安全,软硬深度结合,不怕被抄袭了。

库克2

看完这个例子,你知道我要说什么了吧?既然公共的这么不安全,那我们干脆自己搞一个吧,这就是"内部类"了。

内部类怎么搞?

其实很简单,我们把class的定义移动到TalkingClock内部即可。

重要的是我们的内部类是支持private关键字的,也就是说,他在包内是完全不可见的,这非常安全;另外,由于它处于TalkingClock的内部,所以也不需要给beep设置getter,内部类可以直接访问外围类的数据。


public class TalkingClock {
    private int interval;
    private boolean beep;
    public TalkingClock(int interval, boolean beep) {
        this.interval = interval;
        this.beep = beep;
    }
public void  start() {
    new Timer(interval,new TimePrinter()).start();
    //new Timer(interval,this.new TimePrinter()).start(); this是可以省略的
}
    private  class TimePrinter implements ActionListener {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("At the tone,the time is " + new Date());
            //内部类可以直接访问private beep
            if (beep) Toolkit.getDefaultToolkit().beep();
            //if (TalkingClock.this.beep) Toolkit.getDefaultToolkit().beep(); TalkingClock.this是可以省略的
        }
    }

}

不过在这里要特别提示的是,内部类不会随着外围类的实例化而实例化,如果需要实例化它,那么必须调用其构造器。

没有不透风的墙

要说明的是,这种内部类并不是绝对的安全。

内部类并不是一个虚拟机机制,而只是编译器的把戏罢了,所以如果黑客非常清楚class的结构,是可以调用虚拟机指令获取内部类数据的。

不过不用过分担心,在大多数时候,安全性已经足够了。

具体"编译器"玩的什么把戏,这里就不介绍了,稍微有点复杂,用到再详细说。

得寸进尺-匿名内部类

好了,我们已经解决了安全的问题,我们需要更简洁一些。

比喻:很多时候,我们需要给东西起一个"名字",主要原因是因为我们需要多次用到它。

如果一个东西,我们只用它一次,你就懒得给他起一个特殊的名字。

比如,上茅房的时候,我们都会用手纸屁股,你不会给每一张手纸起一个名字的,因为没有必要,他很快就会进入马桶了,这辈子你都见不到他了。

同理,但很多情况下,我们只需要用到类的实例一次,那么我们就直接舍弃名字,这样会方便好多。这被称为"匿名内部类"。

class TalkingClockAnonymous {
private int interval;
private boolean beep;
    public static void main(String[] args) {
        new TalkingClockAnonymous(1000,true).start();
        JOptionPane.showMessageDialog(null, "Quit program?");
        System.exit(0);
    }
    public TalkingClockAnonymous(int interval, boolean beep) {
        this.interval = interval;
        this.beep = beep;
    }
    public void  start() {
        //匿名内部类
    ActionListener actionListener= new ActionListener(){
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("At the tone,the time is " + new Date());
            if (beep) Toolkit.getDefaultToolkit().beep();
        }
    };

    new Timer(interval,actionListener).start();
}

}

人心向简-lambda表达式

对于一块手纸来说,我们还是搞得太繁琐了,竟然只用一次,那么连他的对象我们也干脆省略了名字吧。

另外,我们也不需要指定方法的参数,因为很明显,只要我们传入的地方要求ActionListener协议,那么方法的参数就一定是ActionEvent了。

鲁迅
public class TalkingClocklambda {
    public static void main(String[] args) {
        new TalkingClocklambda().start(1000, true);
        JOptionPane.showMessageDialog(null, "Quit program?");
        System.exit(0);
    }

    public void start(final int interval, final boolean beep) {
        new Timer(interval, e -> {
            System.out.println("At the tone,the time is " + new Date());
            if (beep) Toolkit.getDefaultToolkit().beep();
        }).start();
    }
}

End

心如止水是Java/AHK持续学习者,欢迎您来和我探讨Java/AHK问题 _

版权声明:

该文章版权系“心如止水”所有,欢迎分享、转发,但如需转载,请联系QQ:2531574300,得到许可并标明出处和原链接后方可转载。未经授权,禁止转载。版权所有 ©心如止水 保留一切权利。

心如止水
上一篇 下一篇

猜你喜欢

热点阅读