Lambda表达式和匿名函数作用一样,相当于传递一个方法,只是更简单。应用于通常是指需要一个函数,但是又不想费神去命名一个函数的场合下使用。如果说,⼀个接口中,要求实现类必须实现的抽象方法,有且只有⼀个!这样的接口,就是函数式接口。
一、语法
格式定义
标准格式:(parameter)->{expression or statement},Lambda表达式组成三要素:括号,箭头,代码块。
Lambda表达式的代码分析
● (): 里面没有内容,可以看成是方法形式参数为空,这里的参数也可以传递一个泛型
● ->:用箭头指向后面要做的事情
● {}: 包含一段代码,我们称之为代码块,可以看成是方法体中的内容
public void name() {
//匿名内部类的方式启动一个线程
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"线程启动啦");
}
}).start();
//等价于下列代码,如果是一行代码,{}可以省略掉
new Thread( ()-> System.out.println(Thread.currentThread().getName()+"线程启动啦") ).start();
}
以上语法在下列情况时可以再简写一些。
public class LamDemo01 {
public static void main(String[] args) {
//正常写法
useFun((String s)->{ System.out.println(s); });
//参数的类型可以省略
useFun((s)->{ System.out.println(s);});
//单个参数可以省略括号
useFun(s-> {System.out.println(s);});
//代码块中单个语句可以省略大括号
useFun(s-> System.out.println(s));
}
}
简单例子
最好是用@FunctionalInterface做下声明,防止由于lambda的一个接口方法的限制破坏了程序代码
@FunctionalInterface
public interface DoSomethingInterface<T> {
String doSomeThing(T t);
}
//test01
@Test
public void name3() {
DoSomethingInterface<String> t = (str) -> {
return str;
};
System.out.println( t.doSomeThing("print") );
}
//test02
@Test
public void name4() {
useDoSomethingInterface( (str) -> {return (String) str;});
}
public static void useDoSomethingInterface(DoSomethingInterface first01){
String a = first01.doSomeThing("print");
System.out.println(a);
}
和匿名内部类的区别
|
匿名内部类 |
Lambad |
所需类型
|
可以是:接口、抽象类、或者具体类 |
只能是:接口 |
使用限制 |
接口中可以多个或者一个方法 |
接口中的只能有一个方法,但可以用default来修饰实现多个方法,具体可参考Consumer.java接口的实现 |
实现原理 |
产生单独的 .class 文件 |
不会有单独的 .class 文件出现,对应的字节码会在运行的时候动态生成 |
二、Java8中抽象的基本的函数式接口
@Data
public class UserBo {
private String name;
private int age;
}
java.util.function.Consumer 和 BiConsumer-消费型
消费者,接口则正好与Supplier接口相反,它不是生产一个数据,而是消费一个数据,这个接口中还有一个andThen方法
Consumer<String> consumer = s-> System.out.println(s);
consumer.accept("Java3y");
@Test
public void test(){
UserBo user = new UserBo();
save(user, t->{ System.out.println(t); });
}
public void save(UserBo user, Consumer<> consumer){
consumer.accept(user);
}
复杂一点的例子,如果UserBo实例分类型就可以声明多个不同的处理实现,代替上面System.out.print()处的逻辑,实现策略模式如下,也有利于分离逻辑代码 。
java.util.function.Supplier-生产型
生产者,用来获取一个泛型参数指定类型的对象数据。
Supplier<String> supplier = () -> "Java4y";
String s = supplier.get();
@Test
public void test(){
UserBo user = get (() -> {return new UserBo();});
}
public UserBo get(Supplier<UserBo> consumer){
return consumer.get();
}
java.util.function.Function 和 BiFunction-函数型
根据一个类型的数据得到另一个类型的数据,适合作数据转换用。
@Test
public void test(){
UserBo user = apply ("man",(t) -> {
UserBo userBo = new UserBo();
userBo.setName("man");
return userBo;
});
}
public UserBo apply(String name, Function<String, UserBo> consumer){
return consumer.apply(name);
}
java.util.function.UnaryOperator 和 和 BiUnaryOperator -算子函数
一种特殊类型的Function,它的入参和出差是一样的,而Function的出入参是不一样的。它的抽象实现如下:
@FunctionalInterface
public interface UnaryOperator<T> extends Function<T, T> {
static <T> UnaryOperator<T> identity() {
return t -> t;
}
}
java.util.function.Predicate 和 BiPredicate -断言型
确定类型为T 的参数是否满足test逻辑,用于做逻辑判断和验证用
@Test
public void test(){
UserBo user = new UserBo();
boolean validate = test(user, t -> {return t.getName().equals("liudong");});
}
public boolean test(UserBo user, Predicate<UserBo> consumer){
return consumer.test(user);
}
}
java.util.Optional-返回值
针对NullPointerException 问题, Java 8 引入了一个新的容器类 Optional,可以代表一个值存在或不存在,这样就不用返回容易出问题的 null。之前文章的代码中就经常出现这个类,也是针对这个问题进行的改进。Optional 类比较常用的几个方法有:
isPresent() :值存在时返回 true,反之 flase
get() :返回当前值,若值不存在会抛出异常
orElse(T) :值存在时返回该值,否则返回 T 的值
Optional 类还有三个特化版本 OptionalInt,OptionalLong,OptionalDouble,刚刚讲到的数值流中的 max 方法返回的类型便是这个
Optional 类其中其实还有很多学问,讲解它说不定也要开一篇文章,这里先讲那么多,先知道基本怎么用就可以。
三、特殊用法
工具类的写法
Consumer<String> consumer1 = System.out::println;
consumer1.accept("www");
或
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("Java3y");
方法引用的写法
前提必须是用@FunctionalInterface ,声明过的或是符合Lambda的标准的接口方法。
// 静态方法引用--通过类名调用
Consumer<String> consumerStatic = PopUtils::parse; //PopUtils类就是一个普通的工具类
consumerStatic.accept("3y---static");
//实例方法引用--通过实例调用
Caclulator cac = new Caclulator(); //是一个非静态的类
Consumer<String> consumer = cac::getDay;
consumer.accept("3y---instance");
// 构造方法方法引用--无参数
Supplier<Java3y> supplier = Caclulator::new; //Caclulator类中必须有一个无参的的构造函数
System.out.println(supplier.get());