🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
> 假设一个情景: 找出满足条件的Hero > 本教程将从使用普通方法,匿名类,以及Lambda这几种方式,逐渐的引入Lambda的概念 ## 步骤 1 : 普通方法 TestLambda.java: ``` package lambda; import java.util.ArrayList; import java.util.List; import java.util.Random; public class TestLambda { public static void main(String[] args) { Random r = new Random(); List<Hero> heros = new ArrayList<Hero>(); for (int i = 0; i < 10; i++) { heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100))); } System.out.println("初始化后的集合:"); System.out.println(heros); System.out.println("筛选出 hp>100 && damage<50的英雄"); filter(heros); } private static void filter(List<Hero> heros) { for (Hero hero : heros) { if (hero.hp > 100 && hero.damage < 50) { System.out.println(hero); } } } } ``` ## 步骤 2 : 匿名类方式 首先准备一个接口HeroChecker,提供一个test(Hero)方法 ``` package lambda; public interface HeroChecker { public boolean test(Hero h); } ``` 然后通过匿名类的方式,实现这个接口 ``` HeroChecker checker = new HeroChecker() { public boolean test(Hero h) { return (h.hp>100 && h.damage<50); } }; ``` 接着调用filter,传递这个checker进去进行判断,这种方式就很像通过Collections.sort在对一个Hero集合排序,需要传一个Comparator的匿名类对象进去一样。 ``` package lambda; import java.util.ArrayList; import java.util.List; import java.util.Random; public class TestLambda { public static void main(String[] args) { Random r = new Random(); List<Hero> heros = new ArrayList<Hero>(); for (int i = 0; i < 10; i++) { heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100))); } System.out.println("初始化后的集合:"); System.out.println(heros); System.out.println("使用匿名类的方式,筛选出 hp>100 && damage<50的英雄"); HeroChecker checker = new HeroChecker() { @Override public boolean test(Hero h) { return h.hp > 100 && h.damage < 50; } }; filter(heros, checker); } private static void filter(List<Hero> heros, HeroChecker checker) { for (Hero hero : heros) { if (checker.test(hero)) { System.out.println(hero); } } } } ``` ## 步骤 3 : Lambda方式 使用Lambda方式筛选出数据 `filter(heros,(h)->h.hp>100 && h.damage<50); ` 同样是调用filter方法,从上一步的传递匿名类对象,变成了传递一个Lambda表达式进去 `h->h.hp>100 && h.damage<50 ` 咋一看Lambda表达式似乎不好理解,其实很简单,下一步讲解如何从一个匿名类一点点演变成Lambda表达式 ``` package lambda; import java.util.ArrayList; import java.util.List; import java.util.Random; public class TestLambda2 { public static void main(String[] args) { Random r = new Random(); List<Hero> heros = new ArrayList<Hero>(); for (int i = 0; i < 10; i++) { heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100))); } System.out.println("初始化后的集合:"); System.out.println(heros); System.out.println("使用Lambda的方式,筛选出 hp>100 && damage<50的英雄"); filter(heros, h -> h.hp > 100 && h.damage < 50); } private static void filter(List<Hero> heros, HeroChecker checker) { for (Hero hero : heros) { if (checker.test(hero)) { System.out.println(hero); } } } } ``` ## 步骤 4 : 从匿名类演变成Lambda表达式 Lambda表达式可以看成是匿名类一点点演变过来 1. 匿名类的正常写法 ``` HeroChecker c1 = new HeroChecker() { public boolean test(Hero h) { return (h.hp>100 && h.damage<50); } }; ``` 2. 把外面的壳子去掉 只保留方法参数和方法体 参数和方法体之间加上符号 -> ``` HeroChecker c2 = (Hero h) ->{ return h.hp>100 && h.damage<50; }; ``` 3. 把return和{}去掉 ``` HeroChecker c3 = (Hero h) ->h.hp>100 && h.damage<50; ``` 4. 把 参数类型和圆括号去掉(只有一个参数的时候,才可以去掉圆括号) ``` HeroChecker c4 = h ->h.hp>100 && h.damage<50; ``` 5. 把c4作为参数传递进去 ``` filter(heros,c4); ``` 6. 直接把表达式传递进去 ``` filter(heros, h -> h.hp > 100 && h.damage < 50); ``` ``` package lambda; import java.util.ArrayList; import java.util.List; import java.util.Random; public class TestLambda1 { public static void main(String[] args) { Random r = new Random(); List<Hero> heros = new ArrayList<Hero>(); for (int i = 0; i < 10; i++) { heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100))); } System.out.println("初始化后的集合:"); System.out.println(heros); System.out.println("使用匿名类的方式,筛选出 hp>100 && damage<50的英雄"); // 匿名类的正常写法 HeroChecker c1 = new HeroChecker() { @Override public boolean test(Hero h) { return (h.hp > 100 && h.damage < 50); } }; // 把new HeroChcekcer,方法名,方法返回类型信息去掉 // 只保留方法参数和方法体 // 参数和方法体之间加上符号 -> HeroChecker c2 = (Hero h) -> { return h.hp > 100 && h.damage < 50; }; // 把return和{}去掉 HeroChecker c3 = (Hero h) -> h.hp > 100 && h.damage < 50; // 把 参数类型和圆括号去掉 HeroChecker c4 = h -> h.hp > 100 && h.damage < 50; // 把c4作为参数传递进去 filter(heros, c4); // 直接把表达式传递进去 filter(heros, h -> h.hp > 100 && h.damage < 50); } private static void filter(List<Hero> heros, HeroChecker checker) { for (Hero hero : heros) { if (checker.test(hero)) { System.out.println(hero); } } } } ``` ## 步骤 6 : 匿名方法 与匿名类 概念相比较, Lambda 其实就是匿名方法,这是一种把方法作为参数进行传递的编程思想。 虽然代码是这么写 ``` filter(heros, h -> h.hp > 100 && h.damage < 50); ``` 但是,Java会在背后,悄悄的,把这些都还原成匿名类方式。 引入Lambda表达式,会使得代码更加紧凑,而不是各种接口和匿名类到处飞。 ## 步骤 7 : Lambda的弊端 Lambda表达式虽然带来了代码的简洁,但是也有其局限性。 1. 可读性差,与啰嗦的但是清晰的匿名类代码结构比较起来,Lambda表达式一旦变得比较长,就难以理解 2. 不便于调试,很难在Lambda表达式中增加调试信息,比如日志 3. 版本支持,Lambda表达式在JDK8版本中才开始支持,如果系统使用的是以前的版本,考虑系统的稳定性等原因,而不愿意升级,那么就无法使用。 Lambda比较适合用在简短的业务代码中,并不适合用在复杂的系统中,会加大维护成本。 ## 练习 把比较器-Comparator,按照从匿名类演变成Lambda表达式的步骤,改写为Lambda表达式 ``` package lambda; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.List; import java.util.Random; public class TestLambda3 { public static void main(String[] args) { Random r = new Random(); List<Hero> heros = new ArrayList<Hero>(); for (int i = 0; i < 10; i++) { heros.add(new Hero("hero " + i, r.nextInt(1000), r.nextInt(100))); } System.out.println("初始化后的集合:"); System.out.println(heros); System.out.println("使用匿名类的方式,筛选出 hp>100 && damage<50的英雄"); // Collections.sort(heros); Comparator<Hero> c = new Comparator<Hero>() { @Override public int compare(Hero o1, Hero o2) { if (o1.hp > o2.hp) { return 1; } else { return -1; } } }; // Collections.sort(heros, c); Collections.sort(heros, (h1, h2) -> (h1.hp >= h2.hp) ? 1 : -1); System.out.println("*****************排序过后********************"); for (Hero hero : heros) { System.out.println(hero); } System.out.println("*****************排序结束********************"); filter(heros, h -> h.hp > 0 && h.damage > 40); } private static void filter(List<Hero> heros, HeroChecker checker) { for (Hero hero : heros) { if (checker.test(hero)) { System.out.println(hero); } } } } ``` > 并不是所有接口都可以使用Lambda表达式,只有函数式接口可以。 按照Java8函数式接口的定义,其只能有一个抽象方法,否则就不是函数时接口,就无法用Lambda表达式。 可以使用@FunctionalInterface标注函数式接口,在编译时提前发现错误。