Java8新特性——Lembda表达式-> 和方法引用、构造器引用、数组引用 :: 的写法说明
一、Lambda表达式的使用
- 举例:
(o1,02) -> Integer.compare(o1,o2);
- 格式:Lambda操作符或箭头操作符
左边:lambda形参列表(其实就是接口中的抽象方法的形参列表)
右边:Lambda体(其实就是重写的抽象方法的方法体) - Lambda表达式的使用(分为6种情况介绍)
@Test
public void test(){
Runnable runnable = new Runnable() {
@Override
public void run() {
System.out.println("i love java");
}
};
runnable.run();
//左边形参列表,右边是方法体
Runnable runnable1 = () -> {System.out.println("i love Object c")};
runnable1.run();
}
@Test
public void test3(){
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
consumer.accept("谎言和誓言的区别是什么呢?");
Consumer<String> consumer1 = (String s) -> {
System.out.println(s);
};
consumer1.accept("一个是听的人当真了,一个是说的人当真了。");
}
Consumer<String> consumer1 = (String s) -> {
System.out.println(s);
};
consumer1.accept("一个是听的人当真了,一个是说的人当真了。");
//优化之后
Consumer<String> consumer1 = (s) -> {
System.out.println(s);
};
consumer1.accept("一个是听的人当真了,一个是说的人当真了。");
-
关于类型推断实际上我们经常使用
(1)List<String> list = new ArrauList<>();
后面的不需要再指明泛型。
(2)int arr[] = {1,2,3}
而不需写成int arr [] = new int(){1,2,3}
-
语法格式四:Lambda若只需要一个参数时,则参数的小括号可以省略
Consumer<String> consumer1 = (s) -> {
System.out.println(s);
};
consumer1.accept("一个是听的人当真了,一个是说的人当真了。");
Consumer<String> consumer1 = s -> {
System.out.println(s);
};
consumer1.accept("一个是听的人当真了,一个是说的人当真了。");
@Test
public void test4(){
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
}
};
System.out.println("*************");
Comparator<Integer> comparator1 = (o1,o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
@Test
public void test5() {
Comparator<Integer> comparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return o1.compareTo(o2);
}
};
System.out.println("*************");
Comparator<Integer> comparator1 = (o1, o2) ->
o1.compareTo(o2);
}
总结:
-
左边:lambda形参列表的参数类型可以省略(类型推断):如果lambda形参列表只有一个参数,其一对( )则可以省略
-
右边:lambda 体应该使用一对{ }包上,如果lambda体只有一条执行语句(可能是return语句),省略一对{ }和return
-
lambda表达式的使用对象都是接口,相当于为接口提供具体的一个实现类的对象时使用。
二、方法引用
- 如果一个接口中,只声明了一个抽象方法,则此接口就称为函数式接口。
@FunctionalInterface
注解声明为函数式接口。
其实不加该接口也是函数式接口,只不过加了该注解就只能有一个方法。(想当于是一种校验)
函数式接口中只有一个抽象方法。
通过Lambda创建函数式接口的对象
-
Lambda表达式的本质就是作为函数式接口的实例。
-
Java最初一直倡导万事万物皆对象,但是随着时代的发展,其他语言的出现,java现在不尽支持OOP还支持OOF(面向函数编程)
-
但是、在Java中Lambda表达式还是对象,不是函数。
-
简单来说,在java8中,Lambda表达式就是一个函数式接口的实例。所以以前用匿名实现类表示的现在都可以用Lambda表达式来写。
-
在java.utils.function中有大量的函数是接口。
- 比如:
Consumer<String> consumer = new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
};
- 以上是一个消费型接口,接受参数但是不返回。
以上是Java内置的四种核心函数式接口
-
消费性接口 Consumer:接受参数,但是返回值是void
-
供给型接口 Supplier:不接收参数,但是有返回值
-
函数型接口 Function<T,R>:接收任意类型的参数,有返回值
-
断定型接口 Predicate:接收参数,返回boolean
-
此外针对四种基本的函数式接口进行了其他的变形
-
这些接口在实例化的时候都可以使用Lambda表达式
-
函数式接口的简单实用例子:
-
Consumer的简单使用
@Test
public void test6(){
spendMoney(9999.99, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("下班后去KTV消费" + aDouble + "元");
}
});
//实用lambda表达式来写
spendMoney(8888.88, money -> System.out.println("下班后去KTV消费" + money + "元"));
}
public void spendMoney(double money, Consumer<Double> doubleConsumer){
doubleConsumer.accept(money);
}
- Predicate的简单使用
@Test
public void test8(){
List<String> list = Arrays.asList("北京","东京","南京","东北","河南","suitianshuang","吴京");
List<String> returnList = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("京");
}
});
System.out.println(returnList);
//实用lamada表达式来写
List<String> returnList1 = filterString(list, s -> s.contains("京"));
System.out.println(returnList1);
}
//将list传入断定型接口中进行某一种规则的断定。
public List<String> filterString(List<String> list, Predicate<String> predicate){
List<String> returnList = new ArrayList<>();
for (String o: list) {
if(predicate.test(o)){
returnList.add(o);
}
}
return returnList;
}
三、方法引用(终极大招)
-
当要传递给Lambda体的操作,**已经有实现的方法了,**就可以使用方法引用!
-
方法引用可以说是Lambda表达式的深层次表达,换句话说,方法引用就是Lambda表达式。也是函数式接口的一个实例,通过方法的名字来指定一个方法,可以认为是Lambda表达式的一个语法糖。
-
要求:实现接口的抽象方法的参数列表和返回值类型,必须与方法引用的方法的参数列表和返回值类型保持一致。
比如:
Cousumer<String> con = str -> System.out.println(str);
con.accept("hello");
上述代码中的Consumer 的返回值是void,方法为accept(T),而PrintStream中的void println(T)方法的返回值和参数列表都一致,这个时候就可以使用方法引用。
方法引用的格式(请在精神集中的情况下看)
- 使用格式:
对象 :: 实例方法 (类或者对象 :: 方法名)
具体分为三种:
(1) 对象 :: 非静态方法
(2) 类 :: 静态方法
(3) 类 :: 非静态方法
第一种格式 对象 :: 非静态方法
Cousumer<String> con = str -> System.out.println(str);
con.accept("hello");
// ********************
printStream ps = System.out;
//参数str也可以省略,因为参数列表一致。
Consumer<String> con = ps :: println;
con.accent("suitianshuang");
- getName()方法的返回值为String类型,没有参数,而抽象方法的返回值也是String,也没有参数,这个时候就可以使用方法引用。
Employee emp = new Employee("1001", "suitianshuang", "23" , "10000");
Supplier<String> sup = -> emp.getName();
System.out.println(sup.get());
//******************
Supplier<String> sup = emp::getName;
System.out.println(sup.get());
第二种格式 类 :: 静态方法
Comparator 中的 int compare(T t1,T t2);
Integer 中的 int Compare(T t1,T t2)
Cpmparator<Integer> com1 = (t1,t2) -> Integer.compare(t1,t2);
com.compare(12,3);
//**********************
//引用的类和抽象方法的返回值和形参列表都一样所以省略了形参列表
Cpmparator<Integer> com2 = Integer::compare;
com.compare(12,3);
@Test
@Test
public void test9(){
Function<Double, Long> function = new Function<Double, Long>() {
@Override
public Long apply(Double aDouble) {
return Math.round(aDouble);
}
};
System.out.println(function.apply(10.5));
//*******************
Function<Double, Long> function1 = d -> Math.round(d);
System.out.println(function1.apply(10.6));
//*******************
Function<Double, Long> function2 = Math::round;
System.out.println(function2.apply(10.1));
}
第三种格式 类 :: 实例方法 (有难度)
Comparator 中的 int compare(T t1 ,T t2)
String 中的 int t1.compareTo(t2)
虽然参数列表不同,但是参数1作为参数2的调用者,这个时候也可以使用方法引用,使用类::实例方法的格式书写。
@Test
public void test10(){
Comparator<String> com1 = (o1, o2) -> o1.compareTo(o2);
System.out.println(com1.compare("abc","aaa"));
//****************
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("aaa","bbb"));
}
BiPredicate 中的 boolean test(T t1, T t2);
String 中的 boolean t1.equals(t2)
@Test
public void test11(){
BiPredicate<String,String> biPredicate = (s1,s2) -> s1.equals(s2);
System.out.println(biPredicate.test("suitianshuang","shuangtiansui"));
//*****************
BiPredicate<String,String> biPredicate1 = String::equals;
System.out.println(biPredicate1.test("suitianshuang","shuangtiansui"));
}
@Test
public void Test12(){
Employee employee = new Employee("1001","suitianshuang","23","10000");
Function<Employee,String> function1 = e -> e.getName();
System.out.println(function.apply(employee));
//***************
Function<Employee,String> function2 = Employee::getName;
System.out.println(function1.apply(employee));
}
四、构造器引用
- 无参构造器
Supplier中的 T get();
Employee的空参构造器 Employee()
Employee 的参数列表以及创建的对象相当于get 方法的返回值 T(泛型),所以可以使用方法引用。
@Test
public void test(){
Supplier<Employee> sup = new Supplier<Employee>(){
@Override
public Employee get(){
return new Employee();
}
};
//*****************
//Lambda表达式
Supplier<Employee> sup1 = () -> new Employee();
System.out.println(sup1.get());
//*****************
//构造器引用
Supplier<Employee> sup2 = Employee::new;
System.out.println(sup2.get());
}
- 有参构造器 (一个参数)
Function 中的 apply(T t);
@Test
public void test(){
Function<Integer,Employee> fun1 = id -> new Employee(id);
Employee employee = fun1.apply(100);
System.out.println(employee);
//****************
//构造器引用
Function<Integer,Employee> fun2 = Employee ::new;
Employee employee = fun2.apply(100);
System.out.println(employee);
}
- 有参构造器(两个参数)
BiFunction 中的 R apply(T t ,U u);
Employee 中的 两个参数的构造器。
@Test
public void test(){
BiFunction<Integer,String,Employee> fun1 = (id,name) -> new Employee(id,name);
System.out.println(fun1.apply(1001,"suitianshuang"));
//******************
BiFunction<Integer,String,Employee> fun2 = Employee::new;
System.out.println(fun2.apply(1001,"suitianshuang"));
}
- 构造器引用总结:
>和方法引用类似,函数是接口的抽象方法的形参列表和构造器的参数列表一致,
抽象方法的返回值烈性即为构造器所属的类的类型。T 即为 构造器要创建的对象。
五、数组引用
Function 中的R apply(T t)
可以把数组看成是一个特殊的类,则数组引用的写法就和构造器引用一致了。
@Test
public void test(){
Function<Integer,String[]> fun = length -> new String[length];
String[] arr = fun.apply(5);
System.out.println(arr.toString());
//***********************
//数组引用
Function<Integer,String[]> fun = String[]::new;
String[] arr = fun.apply(5);
System.out.println(arr.toString());
}
完了