为什么要使用lambda表达式
关于这点,网上的说法一大堆,但是这个就说一个最浅显的理由,就是简洁,使用Lambda表达式可以让你的代码看起来很简洁,去掉了一堆没有意义的代码,只留下核心的逻辑。也许你会说,我看了Lambda表达式,不但不觉得简洁,反而觉得更乱,看不懂了。那是因为我们还没有习惯,用的多了,看习惯了,就好了。
从函数式接口说起
理解Functional Interface(函数式接口)是学习Java8 lambda表达式的关键所在。
函数式接口的定义其实很简单:任何接口,如果只包含唯一一个抽象方法,那么它就是一个函数式接口。对于函数式接口,我们可以通过lambda表达式来创建该接口的对象。
为了让编译器帮助我们确保一个接口满足函数式接口的要求,也就是说有且仅有一个抽象方法。Java8提供了@FunctionalInterface注解。举个简单的例子,Runnable接口就是一个FI,下面是它的源代码:
@FunctionalInterface
public interface Runnable {public abstract void run();
}
使用@FunctionalInterface注解并不强制要求。但是使用注解会让代码看上去更清楚。
lambda表达式的语法糖
语法糖
Java中lambda表达式的格式:参数、箭头->、一个表达式。
为了演示lambda表达式的语法糖,我们通过Comparator作为例子。在Java8之前,可以借助匿名内部类来实现。
public static List<String> compareTest1(){List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");wordList.sort(new Comparator<String>() {@Overridepublic int compare(String w1, String w2) {return Integerpare(w1.length(), w2.length());}});return wordList;
}
Comparator是个函数式接口,我们可以用Lambda表达式来实现。Lambda表达式,很像一个匿名的方法,只是小括号内的参数列表和花括号内的代码被->分隔开了。
public static List<String> compareTest2(){List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");wordList.sort((String w1, String w2) -> {return Integerpare(w1.length(), w2.length());});return wordList;
}
如果一个lambda表达式的参数类型是可以被推导的,那么就可以省略它们的类型。
public static List<String> compareTest3(){List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");wordList.sort((w1, w2) -> {return Integerpare(w1.length(), w2.length()); });return wordList;
}
如果一个lambda表达式的代码块只是return后面跟一个表达式,那么还可以进一步简化。
public static List<String> compareTest4(){List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");wordList.sort((w1, w2) -> Integerpare(w1.length(), w2.length()));return wordList;
}
如果某个方法只含有一个参数,并且该参数的类型可以被推导出来,你甚至可以省略小括号。
public static List<String> compareTest5(){List<String> wordList = Arrays.asList("lianggzone", "spring", "summer", "autumn", "winter");wordList.forEach(word -> System.out.println(word));return wordList;
}
当我们要在另外一个独立线程中执行一些逻辑时,通常会将代码放在一个实现Runable接口的类的run方法中。
public static void runnableTest1(){wSingleThreadExecutor().execute(new Runnable() { @Overridepublic void run() {for (int i = 0; i < 10; i++) {System.out.println(i);}}});
}
如果lambda表达式没有参数,你仍可以提供一对空的小括号,如同不含参数的方法。
public static void runnableTest2(){wSingleThreadExecutor().execute(() -> {for (int i = 0; i < 10; i++) {System.out.println(i);}});
}
注意点
方法引用
对象::实例对象
有些时候,lambda表达式的代码就只是一个简单的方法调用而已,遇到这种情况,lambda表达式还可以进一步简化为方法引用->操作符将方法名和对象或类的名字分隔开来。
在Java8之前,我们打印List内容,正常是这样做的。
public static void print1(){List<String> wordList = Arrays.asList("liang", "spring", "summer", "autumn", "winter");wordList.forEach(new Consumer<String>() {@Overridepublic void accept(String word) {System.out.println(word);}});
}
因为Consumer是函数式接口,我们通过lambda表达式改造下。
public static void print2(){List<String> wordList = Arrays.asList("lian", "spring", "summer", "autumn", "winter");wordList.forEach(word -> System.out.println(word));
}
如果改造成方法引用,表达式 System.out::println, 等价于word -> System.out.println(word)。
public static void print3(){List<String> wordList = Arrays.asList("lian", "spring", "summer", "autumn", "winter");wordList.forEach(System.out::println);
}
类::实例对象
例如,我们不区分大小写对字符串进行排序。
public static String[] sort1(){String[] words = new String[]{"lianggzone", "spring", "summer", "autumn", "winter"};Arrays.sort(words, (x, y) -> xpareToIgnoreCase(y));return words;
}
如果改造成方法引用,表达式: String::compareToIgnoreCase,等价于(x, y) -> xpareToIgnoreCase(y),第一参数会成为执行方法的对象。
public static String[] sort2(){String[] words = new String[]{"lianggzone", "spring", "summer", "autumn", "winter"};Arrays.sort(words, String::compareToIgnoreCase);return words;
}
对象::静态方法
例如,我们对集合进行排序。
public static List<Integer> sortList1(){List<Integer> wordList = Arrays.asList(21, 53);wordList.sort((w1, w2) -> Integerpare(w1, w2));return wordList;
}
如果改造成方法引用,表达式: Integer::compare,等价于(w1, w2) -> Integerpare(w1, w2),第一参数会成为执行方法的对象。
public static List<Integer> sortList2(){List<Integer> wordList = Arrays.asList(21, 53);wordList.sort(Integer::compare);return wordList;
}
构造器引用
// lambda
words.stream().map(word -> {return new StringBuilder(word);
});
// constructor reference
words.stream().map(StringBuilder::new);
变量作用域
在Java8之前, 内部类只能访问final的局部变量 ,为了适应lambda表达式,Java8放宽了这种限制,只要变量实际上不可变(effectively final)就可以。
public static void effectivelyFinal(){int a = wSingleThreadExecutor().execute(() -> {System.out.println(a);});
}
在lambda表达式中,被引用的变量的值不能被修改。做出这个约束的是有原因的,因为lambda表达式中的变量不是线程安全的。
接口的静态方法
从Java8开始,接口也可以有静态方法了。有了这个语法,我们就可以把和接口相关的帮助方法直接定义在接口里。
比如Function接口就定义了一个工厂方法indentity()。表示, 一个功能接口,可以作为赋值的目标一个lambda表达式或方法参考。
T -函数输入的类型
R -函数的结果的类型
public interface Function<T, R> {static <T> Function<T, T> identity() {return t -> t;}
}
事实上,Java8中,很多接口已经添加了静态方法,例如,Java8中的一个使用案例
public interface Path {public static Path get(String first, more) {Default().getPath(first, more);}
}
所以,重要的事情,再说一遍哟,从Java8开始,接口也可以有静态方法了。
/
本文发布于:2024-02-02 14:58:34,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170685711444563.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |