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

2次阅读

共计 4614 个字符,预计需要花费 12 分钟才能阅读完成。

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;

}
最大的问题是安全 - 内部类
俗话说得好,留得青山在,不怕没柴烧,只要小命在,什么都好说。
安全这个问题永远是最重要的。
前面我们提到了之前方案的两个重要缺点,一个是 ” 麻烦 ”,另一个是 ” 不安全 ”,本着 ” 要事第一 ” 的原则,我们首先解决它的安全性问题。
那我们究竟该如何解决呢?
在移动通信领域,高通垄断了高端基带,所以其他公司主要生产手机就必须交钱。高通基带谁也可以买, 是公共的,厂商很难在这个层面搞创术,就算搞了,也很容易被仿制。
不仅这样,还交大量的专利费,这又被称为 ” 高通税 ”,更可恶的是,专利费不是按照芯片来收, 而是按照整机的售价抽取一定比例。
苹果的老大库克终于坐不住了,他娘的, 老资苹果公司还要看你高通脸色,” 老资不跟你玩了!”。

苹果一气之下放弃了高通的方案, 拉上了同样失意的小兄弟英特尔, 一起搞了一个新的基带, 自研自产自销。
由于是自研技术, 当然不用再交专利费,再添加什么其他功能也非常的安全,软硬深度结合,不怕被抄袭了。

看完这个例子,你知道我要说什么了吧? 既然公共的这么不安全,那我们干脆自己搞一个吧,这就是 ” 内部类 ” 了。
内部类怎么搞?
其实很简单,我们把 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,得到许可并标明出处和原链接后方可转载。未经授权,禁止转载。版权所有 ©心如止水 保留一切权利。

正文完
 0