一、先看一个最经典的例子
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("run...");
}
}).start();
这是我们以前多线程的写法,在匿名类里面实现run方法,你可能觉得没什么但是仔细看看,6行代码才实现了一个功能,输出run...,除了
System.out.println("run...");
这一句,其他的似乎和我们的功能没什么关系
现在有了lambda表达式,妈妈再也不用担心我的学习了:
new Thread(() ->System.out.println("lambda run...")).start();
一行搞定,有的同学可能看出来了,其实
() ->System.out.println("lambda run...")
就相当于
new Runnable() {
@Override
public void run() {
System.out.println("run...");
}
}
那我们是不是可以这样写
Runnable r1 = () -> {System.out.println("run...");};
答案是可以的,但是我们不能这样写
Object r1 = () -> {System.out.println("run...");};
编译时会提示异常:
错误: 不兼容的类型: Object 不是函数接口
我们可以进行类型转换:
Object r1 = (Runnable) () -> {System.out.println("run...");};
这样就可以了,所以lambda表达式可以让我们很方便的写出匿名类,我们实现的时候不需要显示的表明是哪个类的哪个方法,机智的java自己会判断,那就有同学会问,java是怎么判断的呢?答曰:根据方法签名
先来看一下Runnable的源码:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
@FunctionalInterface表明它是一个函数式接口,但是这个注解不是必须的(什么是函数式接口?自己动手搜吧,少年)
我们看一下它的方法签名:方法名为run,入参为空,返回值为void,而我们的实现刚好符合(除了方法名,上面也说了,我们根本不care,调用的时候就需要知道方法名了)
如果我们还有个类似的接口,方法签名完全一样:
interface MyRunnable
{
public abstract void run();
}
我们直接把变量类型改了就可以了
MyRunnable r2 = () -> {System.out.println("Hello Lambda!");};
机智的java会知道你实现的到底是哪个类,再比如以下情况:
public static void test(MyRunnable r) {
r.run();
}
public static void test(Runnable r) {
r.run();
}
我们如果这样调用:
TestLambda.test(() -> {System.out.println("Hello Lambda!");});
编译时就会出现错误:
错误: 对test的引用不明确
想想也知道,java根本不知道你的这个实现是哪个接口的,Runnable?MyRunnable?
根据以上我们可以这样来调用:
TestLambda.test((MyRunnable) () -> {System.out.println("Hello Lambda!");});
明确的指明参数类型就可以了
二、java8已经预先定义了很多常用的函数接口方便我们使用:
Function<T, R>——将T作为输入,返回R作为输出
Predicate<T>——将T作为输入,返回一个布尔值作为输出
Consumer<T>——将T作为输入,不返回任何内容
Supplier<T>——没有输入,返回T
BinaryOperator<T>——将两个T作为输入,返回一个T作为输出
java 8中,特别是在操作java集合时你就会碰到很多这样的接口,到时可不要被他们给搞晕掉了,只要记住使用lambda表达式时我们根本不care方法名就会帮你更加容易的进行理解
三、很多文章也说道可以用方法引用来简化lambda表达式,但是有时会出现歧义的情况,比如Double类的2个方法:
public String toString();
public static String toString(double d);
当我们这样写时:
Function<Double,String> f = Double::toString;
String fi = f.apply(1.2);
System.out.println(fi);
编译器根本不知道该调用哪个,出错如下:
对toString的引用不明确
Double 中的方法 toString(double) 和 Double 中的方法 toString() 都匹配
所以,这种情况我们还是老老实实的用lambda表达式吧:
Function<Double,String> f = (d) -> {return d.toString();};
String fi = f.apply(1.2);
System.out.println(fi);
或者
Function<Double,String> f = (d) -> {return Double.toString(d);};
String fi = f.apply(1.2);
System.out.println(fi);
这样就可以了
个人觉得还是习惯用普通的lambda表达式,方法引用的话有时候不易理解