java8新特性--函数式接口+lambda表达式+方法引用
java8出来这么久了,对它还是不熟悉,这不,最近开始学习了,写点学习笔记
什么是函数式接口
- 只包含一个抽象方法的接口,称为函数式接口
- 我们可以在一个接口上使用 @FunctionalInterface注解,这样做可以检查它是否是一个函数式接口
- 有什么用?Lambda表达式就是一个函数式接口的实例,所以以前用匿名实现类表示的现在都可以用Lambda表达式来写
- 例如Runnable接口就是个函数式接口:
@FunctionalInterface
public interface Runnable {
/**
* When an object implementing interface <code>Runnable</code> is used
* to create a thread, starting the thread causes the object's
* <code>run</code> method to be called in that separately executing
* thread.
* <p>
* The general contract of the method <code>run</code> is that it may
* take any action whatsoever.
*
* @see java.lang.Thread#run()
*/
public abstract void run();
}
lambda表达式初体验
// 以前实现Runnable接口创建线程这么写
Runnable r1 = new Runnable() {
@Override
public void run() {
System.out.println("线程run方法。。。。");
}
};
new Thread(r1).start();
// 用lambda表达式可以这么写
Runnable r2 = ()-> System.out.println("线程run方法。。。。");
new Thread(r2).start();
怎么样?是不是感觉很神奇,很简洁。
再回到函数式接口
下面一起来了解下Java 内置的四大核心函数式接口:
函数式接口 | 参数类型 | 返回类型 | 用途 |
---|---|---|---|
Consumer< T > | T | void | 对类型为T的对象进行操作,方法为void accept(T t) |
Supplier< T > | void | T | 直接返回类型为T的对象,方法为T get() |
Function<T,R> | T | R | 操作T类型的对象,返回R类型的结果,方法为R apply(T t) |
Predicate< T > | T | boolean | 操作T类型的对象,返回布尔值,方法为boolean test(T t) |
以上这四种最基本的函数式接口最好能记住,其他函数式接口基本上都是在它们的基础之上来衍生的。
lambda表达式的几种形式
- 无参无返回值
Runnable r = ()->{System.out.println("hello lambda!")};
//方法的实现只有一句话时,中括号可以省略
Runnable r = ()->System.out.println("hello lambda!");
- 有一个参数无返回值
Consumer<String> c = (String str)->{System.out.println(str);};
//参数类型可以省略,因为有类型推断,只有一个参数时,小括号可以省略
Consumer<String> c = str-> System.out.println(str);
- 两个或以上参数,多条执行语句,有返回值
Comparator<Integer> c =(m,n)->{
System.out.println(m+n);
return Integer.compare(m,n);
};
- 有参数有返回值,一条执行语句
Function<Integer,String> f = i -> {
return i+"";
};
//此时,return和大括号可以省略
Function<Integer,String> f = i -> i+"";
方法引用
我们通常使用lambda表达式来创建匿名方法。然而,有时候我们仅仅是调用了一个已存在的方法,此时就可以用方法引用。
先写一个person类,用于测试
public class Person {
String name;
Integer age;
public static String getSth(Integer i){
return "hello"+i;
}
public Person() {
}
public Person(String name) {
this.name = name;
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
// get/set
情况1:对象调用非静态方法 对象::方法名
Consumer<String> con1 = con -> System.out.println(con);
con1.accept("hello con1");
/* 可以发现
Consumer 中的 void accept(T t);
与 PrintStream 中的 void println(String x) 结构差不多,所以,可以这样写:
*/
PrintStream p = System.out;
Consumer<String> con2 = p::println;
con2.accept("hello con2");
// 再来一个
Person person = new Person("吴邪");
Supplier<String> sup1 = () -> person.getName();
System.out.println(sup1.get());
/*
Supplier 中 T get()
与 Person 中 String getName() 形式是类似的
*/
Supplier<String> sup2 = person::getName;
System.out.println(sup2.get());
情况2:类调用静态方法 类::静态方法
Comparator<Integer> com1 = (o1,o2) -> Integer.compare(o1,o2);
System.out.println(com1.compare(5, 3));
/*
Comparator 的 int compare(T o1, T o2)
与 Integer 的 static int compare(int x, int y) 方法的形式类似,故可以这样写
*/
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(9, 9));
// 再来一个
Function<Integer,String> fun2 = in -> "hello"+in;
System.out.println(fun2.apply(3));
/*
同理,发现 Function 的 R apply(T t) 与我们自定义Person类中的 String getSth(Integer i)的结构类似,所以,可以用方法引用
*/
Function<Integer,String> fun3 = Person::getSth;
System.out.println(fun3.apply(5));
情况3:类调用非静态方法 类::非静态方法 (有难度)
Comparator<String> comparator1 = (o1,o2)->o1.compareTo(o2);
System.out.println(comparator1.compare("abc", "abe"));
/*
这次和之前的有些不同,我们把
Comparator 的 int compare(T o1, T o2)
与 String 的非静态方法 int o1.compareTo(o2) 拿来对应
把compare中的o1作为compareTo方法的调用者,把o2作为compareTo方法的参数
不是很好理解,可以自己多写写,多体会一下
*/
Comparator<String> comparator2 = String::compareTo;
System.out.println(comparator2.compare("bcd", "bca"));
//再来一个例子
Person person = new Person("胖子");
Function<Person,String> func1 = p -> p.getName();
System.out.println(func1.apply(person));
/*
同样,去对比
Function 的 R apply(T t)
与 Person 的 String t.getName()
我们把apply方法的参数t作为getName方法的调用者,没有参数
*/
Function<Person,String> func2 = Person::getName;
System.out.println(func2.apply(person));
情况4:构造器引用与数组引用 类::new 数组::new
//1.无参
Supplier<Person> s1 = ()->new Person();
Supplier<Person> s2 = Person::new;
//2.一个参数
Function<String,Person> f1 = str -> new Person(str);
Function<String,Person> f2 = Person::new;
Person p = f2.apply("小哥");
System.out.println(p.getName());
//3.两个参数
BiFunction<Integer,String,Person> bi1 = (i,s)->new Person(s,i);
BiFunction<String,Integer,Person> bi2 = Person::new;
Person xiaobai = bi2.apply("小白", 18);
System.out.println(xiaobai.getName()+":"+xiaobai.getAge());
/*
综上,应该发现,无论如何都调用的是 Person::new 因为,方法引用不能传递参数,
那么,参数是如何体现的呢?就是通过前面那个函数式接口的泛型类型和个数体现的
*/
//4.数组引用,与构造器引用类似
Function<Integer,String[]> fun1 = i -> new String[i];
String[] strArr1 = fun1.apply(4);
System.out.println(strArr1.length);
Function<Integer,String[]> fun2 = String[]::new;
String[] strArr2 = fun2.apply(3);
System.out.println(strArr2.length);
写在最后:
语法不是逻辑,想不通很正常,多写写,多用用,然后就接受了。