本文分两部分:
- 语法简单说明
- lambda的使用
注:这两部分内容均以类+注释的方式进行说明,并且内容均来自官方教程(https://docs.oracle.com/javase/tutorial/java/javaOO/lambdaexpressions.html)。
第一部分:
/** * 语法说明类 * * lambda表达式包含下面几个要素: * 1、逗号分隔的参数列表,如CheckPerson。test(Person p),其中p表示一个Person的对象实例 * 2、向右箭头 →, 箭头左侧是表达式入参,箭头右侧是表达式本身 * 3、表达式体,包含单个表达式或者一个语句块 * * @author zhuotao * */ public class SyntaxInstruction { public static void main(String[] args) { List<Person> persons = PersonGenerator.generatePerons(); printPerson(persons, p -> p.getAge() > 30 && p.getAge() < 50); }
public static void printPerson(List<Person> persons, CheckPerson cp) { for(Person p : persons) { if(cp.test(p)) System.out.println(p); } } } interface CheckPerson{ public boolean test(Person p); } |
第二部分:
假设一种场景:
A正在开发一个社交网络应用,考虑添加一个功能——管理员可以根据用户(Person)的不同特征,执行不同的操作,比如发送信息。
approaches是A写的不同的实现,从a1 ~ a8,看看每个方法较之前有什么明显的提升。
备注:1> Person 社交网络应用用户类 2> PersonGenerator 用户列表模拟产生类
方法一:
/** * 方法一:创建一个方法,用于查询某个特征的用户 * 1、方法入参: * persons 用户列表 * age 约束条件,最低年龄 * 2、输出打印满足条件的用户信息 * * 不过,总感觉有什么不对,如果有一天功能的约束条件增加该怎么办?比如,“同事满足age<50”的条件 * 所以,写了第二个方法,见a2.Approch2 * * @author zhuotao * */ public class Approch1 { public static void main(String[] args) { List<Person> persons = PersonGenerator.generatePerons(); printPersonOlderThan(persons, 30); }
public static void printPersonOlderThan(List<Person> persons, int age) { for(Person p : persons) { if(p.getAge() > age) { p.printPerson(); } } } } |
方法二:
/** * 方法二:增强方法的限定规则,判断年龄时,用区间进行判断 * 额,这跟第一种方法就是换汤不换药嘛,没什么改进哈~Σ( ° △ °|||)︴ * * 于是,第三种方案应运而生。见 a3/Approach3 * * @author zhuotao * */ public class Approach2 { public static void main(String[] args) { List<Person> persons = PersonGenerator.generatePerons(); printPersonOlderThan(persons, 30, 50); }
public static void printPersonOlderThan(List<Person> persons, int low, int high) { for(Person p : persons) { if(p.getAge() > low && p.getAge() < high) { p.printPerson(); } } } } |
方法三:
/** * 方法三:创建规则判断接口 * 将规则判断独立出业务判断,如果新增规则约束,那么只需要实现CheckPerson,重写test即可 * * 仔细想,要是规则很多,肿么办?肿么办? * 先后有100种规则,是不是要创建100种实现?那类的数量。。。~~~~(>_<)~~~~ 此路不通~ * * 于是,试试第四中方法,见 a4/Approach4 * * @author zhuotao * */ public class Approach3 { public static void main(String[] args) {
List<Person> persons = PersonGenerator.generatePerons(); printPerson(persons, new SearchPersonsByAage());
}
public static void printPerson(List<Person> persons, CheckPerson check) { for(Person p : persons) { if(check.test(p)) p.printPerson(); } } } class SearchPersonsByAage implements CheckPerson { @Override public boolean test(Person p) { return p.getAge() > 30 && p.getAge() < 50; }
} interface CheckPerson { public boolean test(Person p); } |
方法四:
/** * 方法四:使用匿名内部类 * 使用这种方法,再也不用担心规则类的膨胀问题了~哇咔咔~ * * 可是,这真的就是想要的方案么?蛋然不是啦~~ * * 来吧,进入今天的主题——lambda expressions, 见a5/Approach5 * * @author zhuotao * */ public class Approach4 {
public static void main(String[] args) { List<Person> persons = PersonGenerator.generatePerons(); printPerson(persons, new CheckPerson() { @Override public boolean test(Person p) { return p.getAge() > 30 && p.getAge() < 50; } }); } public static void printPerson(List<Person> persons, CheckPerson check) { for(Person p : persons) { if(check.test(p)) p.printPerson(); } } } interface CheckPerson { public boolean test(Person p); } |
方法五:
/** * 方法五,使用lambda表达式 * lambda表达式,需要依赖一个功能接口。这里的功能接口,是指只包含一个抽象方法的接口。如,下面的CheckPerson * * 这个例子,发生了什么,连匿名内部类都不需要了,(~ o ~)~zZ 也太简洁了吧~ * 同样是规则判断,这里只需要一个功能接口,一个表达式就解决了规则判断的问题。 (*^__^*) * * 这就满足了? 还没有结束哦,卡木昂,北鼻 ,随我来,见 a6/Approach6 * * @author zhuotao * */ public class Approach5 { public static void main(String[] args) {
List<Person> persons = PersonGenerator.generatePerons(); printPerson(persons, (Person p) -> p.getAge() > 30 && p.getAge() < 50); // 等价于 //printPerson(persons, p -> p.getAge() > 30 && p.getAge() < 50);
}
public static void printPerson(List<Person> persons, CheckPerson check) { for(Person p : persons) { if(check.test(p)) p.printPerson(); } } } interface CheckPerson { public boolean test(Person p); } |
方法六:
/** * 方法六:使用通用功能接口+lambda表达式 * 知道方法5的问题在哪里么? 我来告诉你哈~~ * 因~为~那~个~功~能~接~口~不~通~用~,如果我判断Person之外的对象,岂不是~ * * 是的,将功能进行通用化处理,参考 Predicate<T> T通用的泛型 * * 该逻辑: printAdmin(amdins, admin -> "xxxxxx".equals(admin.getPrivilegeCode())); * * 在这里lambda的优势已经基本上明了—— * 使用功能接口,替换内部类的使用,降低类膨胀风险; * 表达式语法简单,简洁易懂; * 减少coding * * 以上,只是自己的片面之言,总结未必到位欢迎补充。 * * 看到这,lambda已经基本结束了,不过,如果继续看下面的优化相信你会有更大的收获,见 a7/Approach7 * * @author zhuotao * */ public class Approach6 { public static void main(String[] args) { List<Person> persons = PersonGenerator.generatePerons(); printPerson(persons, (Person p) -> p.getAge() > 30 && p.getAge() < 50); // 等价于 //printPerson(persons, p -> p.getAge() > 30 && p.getAge() < 50);
// 下面只是一个示例,用于说明通用功能接口的优势 // List<Aministrator> amdins = new ArrayList<Aministrator>(); // printAdmin(amdins, admin -> "xxxxxx".equals(admin.getPrivilegeCode()));
} public static void printPerson(List<Person> persons, Predicate<Person> check) { for(Person p : persons) { if(check.test(p)) p.printPerson(); } }
public static void printAdmin(List<Aministrator> admins, Predicate<Aministrator> check) { for(Aministrator admin : admins) { if(check.test(admin)) admin.printAdmin(); } } } class Aministrator { private String name; private String id; private String privilegeCode; public String getName() { return name; } public void setName(String name) { this.name = name; } public String getId() { eturn id; } public void setId(String id) { this.id = id; } public String getPrivilegeCode() { return privilegeCode; } public void setPrivilegeCode(String privilegeCode) { this.privilegeCode = privilegeCode; }
@Override public String toString() { return "Aministrator [name=" + name + ", id=" + id + ", privilegeCode=" + privilegeCode + "]"; }
public void printAdmin() { System.out.println(toString()); }
} interface Predicate<T> { public boolean test(T t); } |
方法七:
/** * 方法七:怎么将lambda表达式贯穿到整个应用呢? * 我稀饭这个标题,因为,这表达的是一种技巧,一种思路 * * 试想,printPerson方法做了什么?1、接收用户列表 2、规则判断 3、输出打印符合条件的用户信息 * 那如果我有相似的逻辑:比如 1、接收用户列表(待处理数据) 2、执行某个规则(lambda表达式) 3、发送用户信息(操作) 该如何操作呢? * 这里还是以List<Person> 作为待处理数据,进行优化 * 看processPersons(persons, p -> p.getAge() > 30 && p.getAge() < 50, p -> p.printPerson()); 这个逻辑,你会发现; * 相同的数据,将规则和规则之后的操作进行了分离。 * * 那啥,是不是很带感,那就继续,见 a8/Approach8 * * 注意: 这里使用了 java.util.function.Consumer, 没看错,在jdk8中新引入的function包 * * 不妨看看Consumer的注释信息(除了accept方法,还有个默认方法,感兴趣的同学可以研究下~) * * ** * Represents an operation that accepts a single input argument and returns no * result. Unlike most other functional interfaces, {@code Consumer} is expected * to operate via side-effects. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #accept(Object)}. * * @param <T> the type of the input to the operation * * @since 1.8 * * @FunctionalInterface //这里使用了功能接口的标注 public interface Consumer<T> {......} * * * @author zhuotao * */ public class Approach7 { public static void main(String[] args) { List<Person> persons = PersonGenerator.generatePerons(); processPersons(persons, p -> p.getAge() > 30 && p.getAge() < 50, p -> p.printPerson());
} public static void processPersons(List<Person> persons, Predicate<Person> check, Consumer<Person> block) { for(Person p : persons) { if(check.test(p)) block.accept(p);; } }
} interface Predicate<T> { public boolean test(T t); } |
方法八:
/** * 方法八:依然是将lambda表达式运用到整个应用 * 整个题目的调调跟方法七很像,其实a8和a7的关系就像是a6和a5的关系,只是用来说明通用性的 * * 看到下面的逻辑,有什么想说的? 我是想说,简直太神奇了~~喵喵的~~神奇的jdk8~哇咔咔~ * * 不过,在新事物(Consumer 和 Function)面前是不是感觉有点不适应,那么接下来就再次见证奇迹吧~ * 见~~~~a9/Approach9 * * 值得注意的是,这里同样使用了jdk8新增的一个类:java.util.function.Function * 该类注释如下: * ** * Represents a function that accepts one argument and produces a result. * * <p>This is a <a href="package-summary.html">functional interface</a> * whose functional method is {@link #apply(Object)}. * * @param <T> the type of the input to the function * @param <R> the type of the result of the function * * @since 1.8 * * @author zhuotao * */ public class Approach8 { public static void main(String[] args) { List<Person> persons = PersonGenerator.generatePerons(); processElements( persons, p -> p.getAge() > 30 && p.getAge() < 50, p -> p.getName(), name -> System.out.println(name)); }
/** * 通用的处理逻辑 * @param source 源数据 * @param tester 通用规则校验函数接口 * @param mapper Funtion mapper,具体请参见function源码,接收一个参数,并返回结果 * @param block */ public static <X, Y> void processElements( Iterable<X> source, Predicate<X> tester, Function <X, Y> mapper, Consumer<Y> block) {
for (X p : source) { if (tester.test(p)) { Y data = mapper.apply(p); block.accept(data); } }
} } interface Predicate<T> { public boolean test(T t); } |
方法九:
/** * 方法九:Stream操作,也就是聚集操作(aggregate operations) * * 先看下面的例子,小伙伴惊呆了么? 神奇的jdk8~ * * 简单来说,下面的处理通过对集合进行了stream处理; stream 就是集合元素的序列,和Collection不同,stream不是用来存储数据的数据结构,它是通过管道(pipeline)从数据源获取数据。 * pipeline是stream操作的序列,比如本例子中的 filter-map-foreach。 换言之,数据仍然是保存在集合中,聚集操作需要数据时实时获取 * * 题外话:collection 除了扩展stream之外,还新增了spliterator parallelStream两个方法,感兴趣的同学,巴拉巴拉源码或者debug下本示例进行了解吧~ * * 到这里lambda表达式入门总算结束了,希望这个专题让大家认识了jdk8的新成员——lambda表达式 * (~ o ~)~zZ 好书湖啦~ * * * @author zhuotao * */ public class Approach9 { public static void main(String[] args) { List<Person> persons = PersonGenerator.generatePerons(); persons .stream() // * Returns a sequential {@code Stream} with this collection as its source. .filter(p -> p.getAge() > 30 && p.getAge() < 50) //Returns a stream consisting of the elements of this stream that match the given predicate. .map(p -> p.getName()) // Returns a stream consisting of the results of applying the given function to the elements of this stream. .forEach(name -> System.out.println(name)); // Performs an action for each element of this stream. }
} interface Predicate<T> { public boolean test(T t); } |