什么是lambda表达式?
Lambda表达式是Java SE 8中一个重要的新特性。lambda表达式允许你通过表达式来代替功能接口。lambda表达式就和方法一样,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。Lambda表达式还增强了集合库。
Java Lambda表达式的一个重要用法是简化某些匿名内部类(Anonymous Classes)的写法。实际上Lambda表达式并不仅仅是匿名内部类的语法糖,JVM内部是通过invokedynamic指令来实现Lambda表达式的。
Lambda表达式语法
Lambda 表达式在Java语言中引入了一个新的语法元素和操作符。这个操作符为 “->”,该操作符被称为 Lambda 操作符或箭头操作符。它将Lambda分为两个部分:
l 左侧:指定了 Lambda 表达式需要的所有参数
l 右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能。
语法格式一:无参无返回值,Lambda体只需一条语句
示例:
Runnable runnable = () -> System.out.println("Hello Lambda");
语法格式二:Lambda需要一个参数。
示例:
Consumer<String> consumer = (x)-> System.out.println(x);
语法格式二拓展:Lambda 只需要一个参数时,参数的小括号可以省略。
示例:
Consumer<String> consumer = x -> System.out.println(x);
语法格式三:Lambda需要两个参数,并且有返回值。
示例:
Comparator<Integer> comparator = (x,y)->{
System.out.println("函数式接口");
return Integer.compare(x,y);
};
语法格式三拓展1:当 Lambda 体只有一条语句时,return 与大括号可以省略。
示例:
Comparator<Integer> com = (x,y)->Integer.compare(x,y);
语法格式三拓展2:Lambda 表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”。
示例:
Comparator<Integer> compara = (Integer x, Integer y)->{ //Integer类型可以省略
System.out.println("函数式接口");
return Integer.compare(x,y);
};
BinaryOperator<Long> add = (Long x, Long y)->x+y;
BinaryOperator<Long> addImplict = (x, y)->x+y; //类型推断
使用Lambda表达式的要求
能够使用Lambda的依据是必须有相应的函数接口。函数接口,是指内部只有一个抽象方法的接口。这一点跟Java是强类型语言吻合,也就是说你并不能在代码的任何地方任性的写Lambda表达式。实际上Lambda的类型就是对应函数接口的类型。Lambda表达式另一个依据是类型推断机制,在上下文信息足够的情况下,编译器可以推断出参数表的类型,而不需要显式指名。
自定义函数接口
自定义函数接口只需要编写一个只有一个抽象方法的接口即可
//自定义函数接口
@FunctionalInterface
interface ConsumerInterface<T>{
void accept(T t);
}
其中@FunctionalInterface是可选的,但加上该标注编译器会帮你检查接口是否符合函数接口规范。就像加入@Override标注会检查是否重载了函数一样。有了上述接口等一,就可以写出如下代码:
ConsumerInterface<String> consumerInterface = str-> System.out.println(str);
consumerInterface.accept("我是自定义函数接口");
输出:
详细例子参考:
package lambda;
import java.util.Arrays;
import java.util.List;
public class Test2 {
public static void main(String[] args) {
TestStream<String> testStream = new TestStream<>();
List list = Arrays.asList("111","222","333");
testStream.setList(list);
testStream.myForEach(str-> System.out.println(str));
}
}
//自定义函数接口
@FunctionalInterface
interface ConsumerInterface<T>{
void accept(T t);
}
class TestStream<T> {
private List<T> list;
public void myForEach (ConsumerInterface<T> consumer) {
for (T t : list) {
consumer.accept(t);
}
}
public void setList(List<T> list) {
this.list = list;
}
}
Java内置四大核心函数式接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
Consumer<T> 消费型接口 | T | void | 对类型为T的队形应用操作,包含方法:void accept(T t); |
Supplier<T> 供给型接口 | 无 | T | 返回类型为T的对象,包含方法:T get(); |
Function<T,R> 函数型接口 | T | R | 对象类型为T的对象应用操作,并返回结果。结果是R类型的对象。包含方法:R apply(T t) |
Predicate<T> 断定型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回Boolean值。包含方法:boolearn test(T t) |
Consumer<T>消费型接口
void accept(T t);
@Test
public void test1(){
hello("marilyn", str-> System.out.println("Hello " + str));
}
public void hello (String st, Consumer<String> com) {
com.accept(st);
}
运行结果:
Supplier<T>供给型接口
T get();
//供给型接口
@Test
public void test2 () {
List<Integer> numList = getNumLsit(2,()->(int)(Math.random()*100));
for (Integer num : numList) {
System.out.print(num + " ");
}
}
//需求:产生指定个数的证书,并放入集合中
public List<Integer> getNumLsit (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>函数型接口
R apply(T t)
//函数型接口
@Test
public void test3 () {
String newStr = strHandler("\t\t\t这是一个函数型接口", (str)->str.trim());
System.out.println(newStr);
String subStr = strHandler("这是一个函数型接口", str -> str.substring(4,7));
System.out.println(subStr);
}
public String strHandler(String str, Function<String,String> function) {
return function.apply(str);
}
运行结果:
Predicate<T>断定型接口
boolean test(T t);
//断定型接口
@Test
public void test4 () {
List<String> list = Arrays.asList("hello", "java8", "Lambda", "www", "ok");
List<String> strList = filterStr(list, s->s.length()>3);
System.out.println(strList);
}
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;
}
运行结果:
其他接口
函数式接口 | 参数类型 | 返回类型 | 用途 |
BiFunction<T,U,R> | T,U | R | 对类型为T,U参数应用操作,返回R类型的结果。包含方法为R apply(T t, U u) |
UnaryOperator<T> (Function子接口) | T | T | 对类型为T的对象进行一元运算,并返回T类型的结果。包含方法为T aply(T t) |
BinaryOperator<T> | T,T | T | 对类型为T的对象进行二元运算,并返回T类型的结果。包含方法为T apply(T t) |
BiConsumer<T,U> (BiFunction子接口) | T,U | void | 对类型为T,U参数应用操作。包含方法为void accept(T t, U u) |
ToIntFunction<T> ToLongFuntion<T> ToDoubleFunction<T> | T | Int Long double | 分别计算int,long,double值的函数 |
IntFuntion<R> LongFunction<R> DoubleFunction<R> | Int Long double | T | 参数分别为int,long,double类型的函数 |
方法引用和构造器引用
方法引用
当要传递给Lambda体内的操作,已经有实现的方法了,就可以使用方法引用了!
方法引用使用的前提条件是什么呢?
- 方法引用所引用的方法的参数列表必须要和函数式接口中抽象方法的参数列表相同(完全一致)。
- 方法引用所引用的方法的的返回值必须要和函数式接口中抽象方法的返回值相同(完全一致)。
方法引用一般有三种格式:
- 实例对象名::实例方法名
- 类名::静态方法名
- 类名::实例方法名
注意:
2和3的区别:若Lambda 的参数列表的第一个参数,是实例方法的调用者,第二个参数(或无参)是实例方法的参数时,格式:类名::实例方法名
引用:使用操作符 “::” 将方法名和对象或类的名字分隔开来。
1.对象::实例方法
//对象::实例方法1
@Test
public void test1() {
PrintStream printStream = System.out;
Consumer<String> consumer = str-> printStream.println(str);
consumer.accept("Hello Lambda!");
System.out.println("--------------------");
Consumer<String> consumer1 = printStream::println;
consumer1.accept("Hello Java8!");
Consumer<String> consumer2 = System.out::println;
}
运行结果:
//对象::实例方法2
@Test
public void test2 () {
Employee employee = new Employee(1,"张三",18,5222.99);
Supplier<String> supplier = ()->employee.getName();
System.out.println(supplier.get());
System.out.println("---------------------");
Employee employee1 = new Employee(2,"李四", 20, 2222.34);
Supplier<String> supplier1 = employee1::getName;
System.out.println(supplier1.get());
}
运行结果:
2.类::静态方法
//类::静态方法1
@Test
public void test3 () {
BiFunction<Double,Double,Double> func = (x,y)->Math.max(x,y);
System.out.println(func.apply(1.5,22.2));
System.out.println("------------------------");
BiFunction<Double,Double,Double> func1 = Math::max;
System.out.println(func1.apply(1.2,1.5));
}
运行结果:
//类::静态方法2
@Test
public void test4 () {
Comparator<Integer> comp = (x,y)->Integer.compare(x,y);
System.out.println(comp.compare(3,9));
System.out.println("-------------------------");
Comparator<Integer> comp1 = Integer::compare;
System.out.println(comp1.compare(3,9));
}
运行结果:
4.类::实例方法
//类::实例方法
@Test
public void test5 () {
BiPredicate<String, String> pred = (x,y)->x.equals(y);
System.out.println(pred.test("abcd","abcd"));
System.out.println("----------------------");
BiPredicate<String, String> pred1 = String::equals;
System.out.println(pred.test("abc","abcd"));
System.out.println("----------------------------");
Function<Employee,String> func1 = e->e.show();
System.out.println(func1.apply(new Employee()));
System.out.println("----------------------------");
Function<Employee,String> func2 = Employee::show;
System.out.println(func2.apply(new Employee()));
}
运行结果
构造器引用
构造器使用的前提是什么?
构造器参数列表要与接口中抽象方法的参数列表一致!
语法格式:
1.类名 :: new
//构造器引用
//Employee勒种必须有一个Employee(int number,String name)的构造器
@Test
public void test6() {
BiConsumer<Integer,String> biConsumer = Employee::new;
biConsumer.accept(3,"王五");
}
数组引用
数组引用和构造器引用基本相同
//数组引用
@Test
public void test7() {
//传统Lambda实现
Function<Integer,int[]> func3 = i->new int[i];
int[] apply = func3.apply(10);
System.out.println(apply.length);
//数组引用实现
func3 = int[]::new;
apply = func3.apply(100);
System.out.println(apply.length);
}
运行结果: