# [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,得到许可并标明出处和原链接后方可转载。未经授权,禁止转载。版权所有 ©心如止水 保留一切权利。
心如止水
网友评论