之前刚学java的时候,直接用1.8的结果项目各种报错,然后换了1.7果然也就没有报错,最近小道消息听说jdk1.8有点稳定了,故下载了最新版的jdk1.8玩玩,官网下载的jdk实在慢的要死,下载了好久,想要下的快点的朋友可以到我的分享网盘这边下载,是最新版的jdk-8u131版本——http://pan.baidu.com/s/1gfvb91h。
废话不多说,回到主题。先看看一个Lambda表达式的小例子:
学过多线程的朋友都知道,我们创建可执行的线程对线总共有4种方式——继承Thread类,实现Runnable接口,实现Callable接口以及使用线程池。
拿实现Runnable接口的实例对象来说,我们以前是这么做的
Runnable r1 = new Runnable(){ //1
@Override //2
public void run(){ //3
System.out.println("匿名内部类"); //4
} //5
}; //6
没错,就是用匿名内部类去实现Runnable接口里面的run抽象方法来创建一个Runnable的实例,用匿名内部类的好处当然有,比如对于不会重复使用的实例,它不需要特地建一个java类来实现这个接口。让人不爽的地方也有:对于Runnable这种只有一个抽象方法的接口,我们很清楚的知道他要创建实例的话需要实现的是run这个抽象方法。因此,上面那段代码真正有用的部分也就是注释标识的1和4的部分。我们再看看用lambda表达式创建Runnable接口的实例:
Runnable r2 = () -> System.out.println("Lambda表达式");
Lambda表达式很简略的就完成了这个过程,Lambda是一个匿名函数,我们可以把Lambda表达式理解为一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。现在我们应该能稍微体会到这个特点了。而Lambda表达式主也要用于替换以前广泛使用的内部匿名类,各种回调,比如事件响应器、传入Thread类的Runnable等。
下面就来介绍介绍Lambda表达式:
一、基本语法
Lambda表达式在java语言中引入了一个新的语法元素和操作符,也就是"->",可以称为箭头操作符或者Lambda操作符,该操作符将Lambda表达式拆分成两部分:
左侧 : 指定了Lambda表达式的参数列表
右侧 : 指定了Lambda体,即Lambda表达式要执行的功能
// 语法格式一:无参数,无返回值
Runnable r1 = () -> System.out.println("Hello Lambda!");
// 语法格式二:有一个参数,并且无返回值
Consumer<String> fun = (x) -> System.out.println(x);
//语法格式三:若只有一个参数,小括号可以省略不写
Consumer<String> fun = x -> System.out.println(x);
//语法格式四:有两个以上的参数,有返回值,并且Lambda体中有多条语句
Comparator<Integer> com =(x,y) -> {
System.out.println("函数式接口");
return Integer.compare(x,y);
};
//语法格式五:若Lambda体中只有一条语句,return和大括号都可以省略不写
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
//语法格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即"数据推断"
(Integer x,Integer y) -> Integer.compare(x,y);//x,y的数据类型Integer可以不写
二、函数式接口
函数式接口指的是只包含一个抽象方法的接口。你可以通过Lambda表达式来创建该接口的对象(若lambda表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)可以在任意函数式接口上是用@FunctionalInterface注解来检查它是否是一个函数式接口。
下面举个函数式接口的例子(当然上面提到的Runnable接口也符合函数式接口):
@FunctionalInterface
public interface MyFunc(){
public int getValue(int num);
}
//当然你也可以在函数式接口中使用泛型
@FunctionalInterface
public interface MyFunc<T>{
public T getValue(T t);
}
将Lambda表达式作为参数传递:
//用上面定义的带泛型的函数式接口当参数
public String toUpperString(MyFunc<String> mf,String str){
return mf.getValue(str);
}
String newString = toUpperString((str) -> str.toUpperCase(),"abcdef");
System.out.println(newString); //ABCDEF
java8中其实已经为我们内置了很多函数式接口:包括四大函数式接口:消费型接口——Consumer<T>、供给型接口——Supplier<T>、函数型接口——Function<T,R>以及断言型Predicate<T>,其他接口为这四个接口的子接口,具体细节如下图
拿四大内置函数式接口举个例子:
//Consumer<T> 消费型接口:
@Test
public void test1(){
consume(100, (m) -> System.out.println("嫖一次花费:"+m));
}
public void consume(double money, Consumer<Double> con){
con.accept(money);
}
//Supplier<T> 供给型接口:
@Test
public void test2(){
List<Integer> numList = getNumList(10,() -> (int)(Math.random()*100));
for (Integer num : numList){
System.out.println(num);
}
}
//产生一些整数,并放入集合中
public List<Integer> getNumList(int num, Supplier<Integer> sup){
List<Integer> list = new ArrayList<>();
for(int i = 0;i < num;i++){
Integer n = sup.get();
list.add(n);
}
return list;
}
//Function<T,R> 函数型接口:
@Test
public void test3(){
String newStr = strHandler(" 我日 ",(str) -> str.trim());
System.out.println(newStr);
String subStr = strHandler("我大skt威武",(str) -> str.substring(1,3));
System.out.println(subStr);
}
//需求:用于处理字符串
public String strHandler(String str, Function<String,String> fun){
return fun.apply(str);
}
//Predicate<T> 断言型接口:
@Test
public void test4(){
List<String> list = Arrays.asList("Hello","atguigu","Lambda","www","ok");
List<String> strList = filterStr(list,(str) -> str.length() > 3);
for(String str : strList){
System.out.println(str);
}
}
//将满足条件的字符串,放入集合中
public List<String> filterStr(List<String> list, Predicate<String> pre){
List<String> strList = new ArrayList<>();
for(String str : list){
if(pre.test(str)){
strList.add(str);
}
}
return strList;
}
}
三、方法引用、构造器引用、数组引用(使用"::"操作符)
①方法引用 :
若Lambda体中的内容有方法已经实现了,我们可以使用"方法引用".
方法引用主要三种使用情况:1、对象::实例方法
2、 类::静态方法
3、 类::实例方法
举个例子:
//对象::实例方法名
@Test
public void test1(){
Consumer<String> con1 = (x) -> System.out.println(x);
Consumer<String> con2 = System.out::println;//对象::实例方法名
con2.accept("abcdef");
}
@Test
public void test2(){
Employee emp = new Employee();//Employee为一个javaBean,成员变量有String类型的name以及Integer类型的age,以及对应的getter、setter方法
Supplier<String> sup1 = () -> emp.getName();
String str = sup1.get();
System.out.println(str);
Supplier<Integer> sup2 = emp::getAge;//对象::实例方法名
Integer num = sup2.get();
System.out.println(num);
}
//类::静态方法名
@Test
public void test3(){
Comparator<Integer> com1 = (x,y) -> Integer.compare(x,y);
Comparator<Integer> com2 = Integer::compare;//类::静态方法名
System.out.println(com2.compare(1,2));
}
//类::实例方法名
@Test
public void test4(){
BiPredicate<String,String> bp1 = (x,y) -> x.equals(y);
BiPredicate<String,String> bp2 = String::equals;//类::实例方法名
}
2、若Lambda参数列表中的第一参数是实例方法的调用者,而第二参数是实例方法的参数,可以使用类::实例方法名
②构造器引用
格式: ClassName::new
老规矩,例子:
//构造器引用
@Test
public void test5(){
Supplier<Employee> sup = () -> new Employee();
//构造器引用方式
Supplier<Employee> sup2 = Employee::new;//无参构造
Employee emp = sup2.get();
System.out.println(emp);
}
@Test
public void test6(){
Function<String,Employee> fun = (x) ->new Employee(x);
Function<String,Employee> fun2 = Employee::new;//一个参数的构造函数
System.out.println(fun2.apply("xxName"));
BiFunction<String,Integer,Employee> fun3 = Employee::new;//两个参数的构造函数
System.out.println(fun3.apply("xxName",11));
}
注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致
③数组引用
格式: Type[]::new
例子:
//数组引用
@Test
public void test7(){
Function<Integer,String[]> fun = (x) -> new String[x];
String[] strs = fun.apply(10);
System.out.println(strs.length);
Function<Integer,String[]> fun2 = String[]::new;
String[] str2 = fun2.apply(20);
System.out.println(str2.length);
}