简介
lambda表达式是Java8的新特性,它免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力,它允许把一个函数作为方法的参数(函数作为参数传递进方法中)使用lambda表达式可以让代码更加的简洁紧凑,符合未来函数式编程的趋势。
普通方式、匿名方式和lambda分别实现线程
//普通方法
public class test2 implements Runnable{
public static void main(String[] args) {
test2 te = new test2();
Thread th = new Thread(te);
th.start();
}
@Override
public void run() {
System.out.println("start to run");
}
}
//匿名方法
public class test2{
public static void main(String[] args) {
Thread th = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("start to run");
}
});
th.start();
}
}
//lambda方式
public class test2 {
public static void main(String[] args) {
Runnable run = ()->System.out.println("start to run");
Thread th = new Thread(run);
th.start();
}
}
}
结论:使用匿名的方式省去了创建对象和重写方法的步骤,而lambda则是对匿名方式的一种优化,简化了匿名方式的写法,使得代码更加简洁紧凑。
lambda的重要特征
- 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。例如:x->2*x
- 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。例如:(x, y) -> x – y
- 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。例如:(int x, int y) -> x + y
- 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值
示例代码
public interface SingleNum {
int single(int i);
}
@FunctionalInterface
public interface AddOperation {
int operation(int a,int b);
//boolean operaion(int a); //添加后测试类报错:The target type of this expression must be a functional interface
}
public class test1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
test1 t = new test1();
System.out.println("single param test result:"+t.singleFun(2, i->i+1));
System.out.println("double param test result:"+t.addOperation(3, 5, (a,b)->a+b));
}
public static int singleFun(int i,SingleNum a) {
return a.single(i);
}
public static int addOperation(int a,int b,AddOperation c) {
return c.operation(a, b);
}
}
函数式接口
lambda通过使用函数式接口来简化代码,那么什么是函数式接口呢?
函数式接口,简单来说,就是只包含一个方法的接口。同时,函数式接口可以用注解**@FunctionalInterface**来修饰,这样可以实时监测你写的接口是否符合函数式接口的要求。
常用的函数式接口有哪些呢?
- Comparator,属于java.util包,有且仅有一个抽象方法:int compare(T o1, T o2);
- Suppiler,生产式接口,抽象方法:T get();
- Consumer,消费型接口,抽象方法:void accept(T t);
- Predicate,断言型接口
- Function,函数型接口,抽象方法:R apply(T t);、一般结合andThen()方法使用。
示例
//Comparator
public static Comparator<String> getComparator(){
return (o1,o2)->o1.length()-o2.length();
}
public static void main(String[] args) {
String arr[]={"a","bb","dddd","ccc"};
System.out.println(Arrays.toString(arr));
Arrays.sort(arr,getComparator()); //Arrays的sort方法使用了Comparator函数作为参数
System.out.println(Arrays.toString(arr));
}
//Suppiler
public static void main(String[] args) {
System.out.println(new SupplierTest().appendString(()->"hello ", "world"));
}
public static String appendString(Supplier<String> sub,String s) {
return sub.get()+s;
}
//Consumer
public static void main(String[] args) {
ComsumerTest te = new ComsumerTest();
te.consumerFun((a)->System.out.println("消费了"+a), 2);
}
public static void consumerFun(Consumer<Integer> com,Integer i) {
com.accept(i);
}
//Predicate
public static void main(String[] args) {
PredicateTest te = new PredicateTest();
boolean b = te.predicate((a)->{
if(a>0) return true;
else return false;
}, 3);
System.out.println(b);
}
public static boolean predicate(Predicate<Integer> p,int i) {
return p.test(i);
}
//Function
public static void main(String[] args) {
FilterTest te = new FilterTest();
String str= te.filter((s)->s.trim(), " hello world");
System.out.println(str);
String st = te.appendStr((s)->s+"haha",(a)->a.trim(), " hello wolrd ");
System.out.println(st);
}
public static String filter(Function<String,String> f,String s) {
return f.apply(s);
}
public static String appendStr(Function<String,String> f,Function<String,String> f2,String s) {
return f.andThen(f2).apply(s); //意思是String s=fun.aplply(s)+"haha",String s1=fun2.apply(s)
}
方法引用
方法引用是指类名或者实例对象通过 ‘::’ 符号直接引用方法,除了调用动作外,没有其他任何多余的动作。
其实它也是lambda的另外一种表现形式或者说是lambda格式的一种优化。
使用场景
我们用Lambda表达式来实现匿名方法。但有些情况下,我们用Lambda表达式仅仅是调用一些已经存在的方法,除了调用动作外,没有其他任何多余的动作,在这种情况下,我们倾向于通过方法名来调用它。
方法引用的分类
- 静态方法引用。格式:类名::静态方法
- 实例方法引用。格式:实例对象::实例方法
- 对象方法引用。格式:类名::实例方法
- 构建方法引用。格式:类名::new
举例
//静态方法引用
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Integer> li = Arrays.asList(1,2,3,4);
li.sort((a,b)->Integer.compare(a, b)); //普通的lambda表达式
li.sort(Integer::compare); //方法引用
}
//实例方法引用
public static void main(String[] args) {
ReferenceTest2 test = new ReferenceTest2();
User user = new User("欧阳峰",32);
Supplier<String> supplier = () -> user.getName();
System.out.println("Lambda表达式输出结果:" + supplier.get());
Supplier<String> supplier2 = user::getName;
System.out.println("实例方法引用输出结果:" + supplier2.get());
}
@Data
class User {
private String name;
private Integer age;
}
//构造方法引用
public static void main(String[] args) {
Supplier<List<Integer>> userSupplier = () -> new ArrayList<>();
List<Integer> li = userSupplier.get();
Supplier<List<Integer>> userSupplier1 = ArrayList::new;
List<Integer> li2 = userSupplier1.get();
}
Stream流
Stream流是Lambda的衍生物,能够简化集合和数组的操作,关注的是做什么,而不是怎么做
获取流的方式
- 对于Collection集合,有一个默认的方法stream方法获取流
- Stream接口中也有方法of来获取流
Stream流的特点
属于管道流,只能被消费一次,第一个Stream流使用完毕,数据会流向下一个Stream流,前一个就会关闭
示例
public static void main(String[] args) {
// TODO Auto-generated method stub
List<Integer> li = Arrays.asList(1,2,3,4,5,9,8);
List<String> li2 = Arrays.asList("a","bb","dddd","ccc");
//截取,排序、过滤和打印
li.stream().limit(5).sorted().filter(a->a!=0).forEach(System.out::println);
System.out.println("--------------");
//过滤、排序和打印
li2.stream().filter(a->!a.isEmpty()).sorted().forEach(System.out::println);
//获取最大值
Optional<Integer> max = li.stream().max((a,b)->{return b-a;});
Integer i = max.get();
System.out.println(i);
}
总结
1、哪里有函数式接口,哪里就能用lambda表达式
2、当涉及到匿名函数或者方法返回的是一个函数式接口时,可以用lambda表达式
3、方法引用的前提是;对象和方法都已经存在\类和方法都存在
4、方法引用是Java8的新特性,它是在lambda的基础上才能够使用,脱离了lambda,则无意义,在某些条件下,是对lambda格式的另一种写法,但是本人不建议使用这个特性,可读性不高,也不便于编程。
5、Stream流也是Java8的新特性,它是流式函数,管道中从前往后执行,最终结束,使用它,方便我们进行流式编程,使得代码更加的优雅整洁,同时,它也是在lambda的基础上进行的操作。