Java 8 新特性&Lambda表达式
接口中的默认方法
Java 8 允许我们给接口中添加一个非抽象的方法,只需要使用default关键字定义即可。
接口中还可以存在静态方法,可以使用 接口名.静态方法名 的形式直接使用
1. Lambda 表达式
1.1 认识Lambda表达式
public class LambdaTest1{
public static void main(String[] args) {
//假如一个list集合中的元素要排序
List<String> list = Arrays.asList("hello","tom","apple","bbc");
//之前的排序我们可以这样写
Collections.sort(list, new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
return -o1.compareTo(o2);
}
});
//使用Lambda表达式
Collections.sort(list,(String s1,String s2)->{
return s1.compareTo(s2);
});
//可以简写为下面这一行
Collections.sort(list,(s1,s2)->s1.compareTo(s2));
System.out.println(list);
}
}
java中,引入了一个新的操作符“->”,该操作符在很多资料中,称为箭头操作符,或者lambda操作符;箭头操作符将lambda分成了两个部分:
左侧:lambda表达式的参数列表
右侧:lambda表达式中所需要执行的功能,即lambda函数体,和返回值类型
1.2 Functional接口
函数式接口:
有且只有一个抽象方法的接口,但是可以另外有其他的默认方法(非抽象方法)和静态方法
【Lambda表达式】本质就是对【函数式接口】中唯一 一个抽象方法进行实现的。
因为 默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。
注意:有多个抽象方法的接口,不能实现lambda表达式
如何保证一个接口是【函数式接口】呢?
在接口上方加上 @FunctionalInterface 注解,如果不满足,此接口就会报错。
例如
//这个注解不加也可以,加上只是为了让编译器检查
@FunctionalInterface
interface Action{
public void run();
default void doSomething(){
System.out.println("doSomething..");
}
}
@FunctionalInterface
interface Work<T,V>{
public V doWork(T t);
}
//类
public class LambdaTest2 {
public static void test(Action a){
a.run();
a.doSomething();
}
public static void run(Work<String,Integer> a){
int i = a.doWork("hello");
System.out.println(i);
}
public static void main(String[] args) {
//原来的内部类实现方式
test(new Action(){
@Override
public void run() {
System.out.println("run..");
}
});
//1.lambda表达式方法
test(()->System.out.println("run"));
//2.也可以先创建对象
Action a = ()->System.out.println("run...");
System.out.println(a.getClass());
test(a);
//接口中有泛型也可以,只关注方法的参数和返回值
Work<String,Integer> w = (v)->v.length();
run(w);
run((v)->v.length());
//如果参数只有一个,那么还可以这样简写: 去掉小括号
//注意代码就一句,作为返回值的话不用写return
run(v->v.length());
//有多句代码,就需要写{}了,并且需要写return
run(v->{
System.out.println("doWork..");
return v.length();
});
}
}
注意:lambda表达式无法表示接口中默认方法的重写,lambda表达式只能去匹配对应接口中的唯一抽象方法。相当于lambda表达式只是对抽象方法的实现。
静态方法和非静态方法的引用
Java 8 允许使用 :: 关键字来传递方法(静态方法和非静态方法)
能引用的条件:
Action接口中只有一个方法且方法的 参数类型 和 返回值类型 与Integer类中的
静态方法toBinaryString的 参数类型、返回类型 是一致的.
那么能引用非静态方法的原因也是这样
public class LambdaTest3 {
public static void main(String[] args) {
//正常是这样使用的
Action a1 = v->"接收的数字为:"+v;
System.out.println(a1.run(5));
//使用Lambda引用Integer类中的静态方法
Action a2 = Integer::toBinaryString;
System.out.println(a2.run(10));
//使用Lambda引用LambdaTest3类的对象中的非静态方法
LambdaTest3 t = new LambdaTest3();
Action a3 = t::test;
System.out.println(a3.run(20));
}
public String test(int i){
return "i="+i;
}
@FunctionalInterface
interface Action{
public String run(int i);
}
}
下面是一个接口中带泛型的时候例子: 可以使用 类名::非静态方法 的形式引用方法
public class LambdaTest3Pro {
public static void main(String[] args) {
Model m = new Model();
//这些写法都可以
//相当于变量v是run方法中接收的形参,然后使用v调用它的方法,v的类型是Model,因为这里使用了泛型
Action3Pro<Model> a1 = v->v.test1();
Action3Pro<Model> a1 = v->v.test2("hello");
Action3Pro<Model> a1 = v->v.test3();
a1.run(m);
//在这种情况下,还可以使用Model引用它的内部的方法
//但是必须满足以下要求:
//1.实现的抽象方法(run)参数类型【必须】Model
//2.引用的方法【必须】是无参的
//将来run方法中就自动会调用所传的对象m这个被引用的方法
Action3Pro<Model> a2 = Model::test1;
a2.run(m);
或者
Action3Pro<Model> a2 = Model::test3;
a2.run(m);
//编译报错,因为test2不是无参的
Action3Pro<Model> a2 = Model::test2;
a2.run(m);
}
}
interface Action3Pro<T>{
public void run(T t);
}
class Model{
//无参无返回值类型
public void test1(){
System.out.println("test1");
}
//有参数没返回值
public void test2(String s){
System.out.println("test2");
}
无参有返回值类型
public int test3(){
System.out.println("test3");
return 1;
}
}
使用 :: 关键字引用构造函数
public class LambdaTest4 {
public static void main(String[] args) {
//Lambda表达式引用构造函数
//根据构造器的参数来自动匹配使用哪一个构造器
//这里执行create方法的时候会自动调用Action4类中的有参构造器
Action4Creater a = Action4::new;
Action4 a4 = a.create("zhangsan");
a4.say();
}
}
class Action4{
private String name;
public Action4() {
}
public Action4(String name) {
this.name = name;
}
public void say(){
System.out.println("name = "+name);
}
}
interface Action4Creater{
public Action4 create(String name);
}
常用的函数式接口
在jdk中通用的函数式接口如下(都在java.util.function包中):
Supplier<String> sp = () -> "hello";//只有输出消息,没有输入参数
Consumer<String> cp = r -> System.out.printf(r);//有一个输入参数,没有输出
Function<Integer, String> func = r -> String.valueOf(r);//有一个输入参数 有一个输出参数
BiFunction<Integer, Integer, String> biFunc = (a, b) -> String.valueOf(a + b);//有两个输入参数 有一个输出参数
BiConsumer<Integer, Integer> biCp = (a, b) -> System.out.printf(String.valueOf(a + b));//有两个输入参数 没有输出参数
java.util.function.Function<T, R> 接口
Function接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法
compose方法表示在某个方法之前执行
andThen方法表示在某个方法之后执行
注意:compose和andThen方法调用之后都会把对象自己本身返回,这可以方便链式编程
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
//注意: t->t是(t)->t的简写
//t->t是作为方法identity的返回值的,也就是Function类型对象
//类似于这样的写法:Function<Object, Object> f = t->t;
//那么f.apply("test") 返回字符串"test"
//传入什么则返回什么
static <T> Function<T, T> identity() {
return t -> t;
}
}
java.util.function.Supplier接口
Supplier接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数
public interface Supplier<T> {
T get();
}
public class SupplierTest {
public static void main(String[] args) {
//生成一个八位的随机字符串
Supplier<String> f = ()->{
String base = "abcdefghijklmnopqrstuvwxyz0123456789";
Random random = new Random();
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 8; i++) {
//生成[0,base.length)之间的随机数
int number = random.nextInt(base.length());
sb.append(base.charAt(number));
}
return sb.toString();
};
System.out.println(f.get());
}
}
java.util.function.Consumer接口
Consumer接口接收一个任意范型的值,和Function接口不同的是该接口没有任何值
public interface Consumer<T> {
void accept(T t);
default Consumer<T> andThen(Consumer<? super T> after) {
Objects.requireNonNull(after);
return (T t) -> { accept(t); after.accept(t); };
}
}
例如:
public class ConsumerTest {
//静态内部类
private static class Student{
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
Student s = new Student();
s.setName("tom");
Consumer<Student> first = stu->{
System.out.println("我要第一个执行,把name值给改了");
stu.setName("zhangsan");
};
Consumer<Student> then = stu->{
System.out.println("我紧跟着执行,输出的name值为:"+stu.getName());
};
first.andThen(then).accept(s);
}
}