JAVA8学习笔记-Lambda
声明:本文仅为学习记录,不保证绝对的准确性
1.一次关于苹果的改良
先定义一个苹果的实体类
public class Apple {
private String color;
private long weight;
//省略了构造函数,getter和setter,toString
}
版本一
这是一段筛选出绿色苹果的代码,缺点很明显,扩展性很差。
public static List<Apple> findGreenApple(List<Apple> apples){
List<Apple> list = new ArrayList<>();
for (Apple apple : apples){
if("green".equals(apple.getColor())){
list.add(apple);
}
}
return list;
}
版本二
版本二就是在版本一的基础上,加了一个入参,颜色。这样可以进一步选择要筛选的颜色,但仍旧很局限。
版本三
准备工作
public interface AppleFilter {
boolean filter(Apple apple);
}
public static List<Apple> findApple(List<Apple> apples, AppleFilter appleFilter){
List<Apple> list = new ArrayList<>();
for (Apple apple : apples){
if(appleFilter.filter(apple)){
list.add(apple);
}
}
return list;
}
//实现类
public static class GreenFilter implements AppleFilter{
@Override
public boolean filter(Apple apple) {
return (apple.getColor().equals("green"));
}
}
版本三定义了一个过滤接口,通过不停的增加这个接口的实现,可以满足各种各样的需求。但是随着需求不停的增加,会多出很多几乎只使用一次的实现类,这样就会显得不优雅。(大佬们都爱拿优雅说事情,跟个风不会挨打吧)
//新定义一个苹果类的list
List<Apple> list = Arrays.asList(new Apple("green",120),
new Apple("red",130),new Apple("green",140));
//传入新定义的实现类
List<Apple> apples = findApple(list, new GreenFilter());
版本四
版本四采用了匿名内部类的方法,但是java8实战的作者说,这样代码还是很多,而且可读性不好。(心里有一丢丢的认同)
List<Apple> apples = findApple(list, new AppleFilter() {
@Override
public boolean filter(Apple apple) {
return apple.getColor().equals("red");
}
});
System.out.println(apples);
版本五
这个就是我们的lambda表达式了,是不是感觉和匿名内部类很像,其实本来就很像,底层实现也很像。但是,代码变少了对不对,对不对!(其实我觉得还行,但是还是向大佬们看齐,绝不向不优雅的代码做出妥协)
List<Apple> apples = findApple(list, apple -> {
return apple.getColor().equals("green");
});
System.out.println(apples);
匿名内部类背锅系列
这段代码是作者为了diss匿名内部类的易读性差的特点贴出来的。
可以看出,核心就在于方法里的this代指的是谁,我们可以看出,this代表runnable类,因此结果是5。
public class MeaningOfThis
{
public final int value = 4;
public void doIt()
{
int value = 6;
Runnable r = new Runnable(){
public final int value = 5;
public void run(){
int value = 10;
System.out.println(this.value);
}
};
r.run();
}
public static void main(String...args)
{
MeaningOfThis m = new MeaningOfThis();
m.doIt();
}
}
2.@FunctionalInterface
这个接口要求类有且只有一个方法,包括继承过来的方法(default方法除外);如果没有标注的话,也可以用lambda表达式(在满足上面要求的情况下)。jdk1.8在很多方法中都加上了这个接口,例如runnable(),comparator()。
runnable()的演示。
//匿名内部类
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}).start();
//lambda表达式
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
这段程序的结果是
Thread-0
Thread-1
Comparator()的演示。
//匿名内部类
Comparator<Apple> byColor = new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
return o1.getColor().compareTo(o2.getColor());
}
};
//lambda表达式
Comparator<Apple> byColor2 = (o1, o2) -> o1.getColor().compareTo(o2.getColor());
//list的sort实现,但没有单独定义comparator
list.sort((a1, a2) -> a1.getColor().compareTo(a2.getColor()));
3.Lambda语法和函数式接口
语法
先总结一下。
(parameters) -> expression
(parameters) -> { statements; }
举例子。
() -> {} // public void run()
() -> "haha" // public String run()
() -> {return "haha";} // public String run()
Predicate
predicate的作用像是筛选用的,或者说做判断。
//Predicate<T> 传入T
private static List<Apple> filter(List<Apple> source, Predicate<Apple> predicate) {
List<Apple> result = new ArrayList<>();
for (Apple a : source) {
if (predicate.test(a))
result.add(a);
}
return result;
}
//运行,筛选出绿色苹果
List<Apple> result = filter(list, (apple) -> apple.getColor().equals("green"));
//LongPredicate 传入一个Long型的值
private static List<Apple> filterByLongPredicate(List<Apple> source,
LongPredicate predicate) {
List<Apple> result = new ArrayList<>();
for (Apple a : source) {
if (predicate.test(a.getWeight()))
result.add(a);
}
return result;
}
//运行,筛选出中重量大于100的苹果
List<Apple> result = filterByLongPredicate(list, w -> w > 100);
//BiPredicate 传入两个参数
private static List<Apple> filterByBiPredicate(List<Apple> source,
BiPredicate<String, Long> predicate) {
List<Apple> result = new ArrayList<>();
for (Apple a : source) {
if (predicate.test(a.getColor(), a.getWeight()))
result.add(a);
}
return result;
}
//运行,选出绿色和大于100克的苹果
List<Apple> result = filterByBiPredicate(list, (s, w) -> s.equals("green") && w > 100);
Consumer
consumer像是拿过来用掉了,没有判断,更没有返回值,单纯自己做一些处理。
//接收T,此处为apple
private static void simpleTestConsumer(List<Apple> source, Consumer<Apple> consumer) {
for (Apple a : source) {
consumer.accept(a);
}
}
//运行,打印传进来的apple
simpleTestConsumer(list, a -> System.out.println(a));
//接收两个T,一个String,一个apple
private static void simpleBiConsumer(String c, List<Apple> source, BiConsumer<Apple, String> consumer) {
for (Apple a : source) {
consumer.accept(a, c);
}
}
//运行,打印传进去的string和apple
simpleBiConsumer("XXX", list, (a, s) -> System.out.println(s + a);
Function
返回什么值,自己定义,最后一个参数为返回值。
//返回值为String,传入apple
private static String testFunction(Apple apple, Function<Apple, String> fun) {
return fun.apply(apple);
}
//运行,只是做了个toString处理
String result = testFunction(new Apple("yellow", 100), (a) -> a.toString());
//返回值为apple,传入string和long
private static Apple testBiFunction(String color, long weight, BiFunction<String, Long, Apple> fun) {
return fun.apply(color, weight);
}
//运行,新建apple对象
Apple a = testBiFunction("Blue", 130, (s, w) -> new Apple(s, w));
Supplier
//没有参数,只有返回值,此处定义为apple
private static Apple createApple(Supplier<Apple> supplier) {
return supplier.get();
}
//运行,初始化一个apple对象
Apple a2 = createApple(() -> new Apple("Green", 100));
//等价于下面,不过下面的是string对象
//TODO,不太懂
Supplier<String> s = String::new;
String abc = s.get();//abc是空字符串
Runnable中用到的变量默认为final
int i = 0;
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(i);//此处的i会报错,因为需要i为final型,下面的i++破坏了这个条件
}
};
i++;
4.函数推导
静态方法
//静态方法,这段代码的作用为把字符串转为数字,Function的两个参数,String为入参,Integer为返回值,parseInt则为Integer的静态方法
Function<String, Integer> f = Integer::parseInt;
Integer result = f.apply("123");
System.out.println(result);
类方法
//类方法,这段代码的作用为找出字符串中特定位置的字符,BiFunction的String和Integer为入参,含义分别为要处理的字符串和要取出字符的位置,Character则为返回值类型,显然调用的String类的成员方法,因为双引号前为类名,而不是实例,所以直接传字符串即可
BiFunction<String, Integer, Character> f2 = String::charAt;
Character c = f2.apply("hello", 2);
System.out.println(c);
实例方法
//实例方法,作用于上面的一致,只不过不用传字符串,因为调用的方法本身就是这个实例的方法。
String string = new String("hello");
Function<Integer, Character> f3= string::charAt;
Character c2 = f3.apply(4);
System.out.println(c2);