java8新特性源码解析
2018-02-01 10:48
381 查看
1 Lambda表达式与Functional接口
Lambda表达式(也称为闭包)是整个Java 8发行版中最受期待的在Java语言层面上的改变,Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中),或者把代码看成数据:函数式程序员对这一概念非常熟悉。在JVM平台上的很多语言(Groovy,Scala,……)从一开始就有Lambda,但是Java程序员不得不使用毫无新意的匿名类来代替lambda。
关于Lambda设计的讨论占用了大量的时间与社区的努力。可喜的是,最终找到了一个平衡点,使得可以使用一种即简洁又紧凑的新方式来构造Lambdas。在最简单的形式中,一个lambda可以由用逗号分隔的参数列表、–>符号与函数体三部分表示。例如:
Arrays.asList("a","b","d").forEach(
e -> System.out.println( e ) );
Arrays.asList( "a", "b", "d" ).forEach( e -> {
System.out.print( e );
System.out.print( e );
} );
Lambda可能会返回一个值。返回值的类型也是由编译器推测出来的。如果lambda的函数体只有一行的话,那么没有必要显式使用return语句。下面两个代码片段是等价的:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
和
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
int result = e1.compareTo( e2 );
return result;
} );
语言设计者投入了大量精力来思考如何使现有的函数友好地支持lambda。最终采取的方法是:增加函数式接口的概念。函数式接口就是一个具有一个方法的普通接口。像这样的接口,可以被隐式转换为lambda表达式。java.lang.Runnable与java.util.concurrent.Callable是函数式接口最典型的两个例子。在实际使用过程中,函数式接口是容易出错的:如有某个人在接口定义中增加了另一个方法,这时,这个接口就不再是函数式的了,并且编译过程也会失败。为了克服函数式接口的这种脆弱性并且能够明确声明接口作为函数式接口的意图,Java
8增加了一种特殊的注解@FunctionalInterface(Java 8中所有类库的已有接口都添加了@FunctionalInterface注解)。让我们看一下这种函数式接口的定义:
@FunctionalInterface
public interface Functional {
void method();
}
需要记住的一件事是:默认方法与静态方法并不影响函数式接口的契约,可以任意使用:
@FunctionalInterface
public interface FunctionalDefaultMethods {
void method();
default void defaultMethod() {
}
}
2. Default关键字
在Java 8中,接口可以包含带有实现代码的方法,这些方法称为default方法。
3. Optional(见5.函数式接口详述)
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google
Guava的启发,Optional类已经成为Java 8类库的一部分。
Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。更多详情请参考官方文档。
我们下面用两个小例子来演示如何使用Optional类:一个允许为空值,一个不允许为空值。
如果Optional类的实例为非空值的话,isPresent()返回true,否从返回false。为了防止Optional为空值,orElseGet()方法通过回调函数来产生一个默认值。map()函数对当前Optional的值进行转化,然后返回一个新的Optional实例。orElse()方法和orElseGet()方法类似,但是orElse接受一个默认值而不是一个回调函数。下面是这个程序的输出:
让我们来看看另一个例子:
下面是程序的输出:
********************************************************************************************************************************************************************************************************************************************************
比如以前你从数据库里获取一个对象,然后对他进行操作,可能是这样的代码
但是用函数式编程来写就该是这样
再举个例子,若你从数据库查出来的对象要是空的话需要报错,以前会这样写
但是现在你可以这样写
还可以简化成
********************************************************************************************************************************************************************************************************************************************************
optional的正确用法!!!
先又不得不提一下 Optional 的三种构造方式: Optional.of(obj), Optional.ofNullable(obj) 和明确的 Optional.empty()
Optional.of(obj): 它要求传入的 obj 不能是 null 值的, 否则还没开始进入角色就倒在了 NullPointerException 异常上了.
Optional.ofNullable(obj): 它以一种智能的, 宽容的方式来构造一个 Optional 实例. 来者不拒, 传 null 进到就得到 Optional.empty(), 非 null 就调用 Optional.of(obj).
那是不是我们只要用 Optional.ofNullable(obj) 一劳永逸, 以不变应二变的方式来构造 Optional 实例就行了呢? 那也未必, 否则 Optional.of(obj) 何必如此暴露呢, 私有则可?
我本人的观点是: 1. 当我们非常非常的明确将要传给 Optional.of(obj) 的 obj 参数不可能为 null 时, 比如它是一个刚 new 出来的对象(Optional.of(new User(...))), 或者是一个非 null 常量时; 2. 当想为 obj 断言不为 null 时, 即我们想在万一 obj 为 null 立即报告 NullPointException 异常,
立即修改, 而不是隐藏空指针异常时, 我们就应该果断的用 Optional.of(obj) 来构造 Optional 实例, 而不让任何不可预计的 null 值有可乘之机隐身于 Optional 中.
存在即返回, 无则提供默认值
存在即返回, 无则由函数来产生
存在才对它做点什么
map 函数隆重登场
当 user.isPresent() 为真, 获得它关联的 orders, 为假则返回一个空集合时, 我们用上面的 orElse, orElseGet 方法都乏力时, 那原本就是 map 函数的责任, 我们可以这样一行
map 是可能无限级联的, 比如再深一层, 获得用户名的大写形式 *map自带空指针判断
这要搁在以前, 每一级调用的展开都需要放一个 null 值的判断
一句话小结: 使用 Optional 时尽量不直接调用 Optional.get() 方法, Optional.isPresent() 更应该被视为一个私有方法, 应依赖于其他像 Optional.orElse(), Optional.orElseGet(), Optional.map() 等这样的方法.
4.Stream
Task类有一个分数的概念(或者说是伪复杂度),其次是还有一个值可以为OPEN或CLOSED的状态.让我们引入一个Task的小集合作为演示例子:
我们下面要讨论的第一个问题是所有状态为OPEN的任务一共有多少分数?在Java 8以前,一般的解决方式用foreach循环,但是在Java 8里面我们可以使用stream:一串支持连续、并行聚集操作的元素。
程序在控制台上的输出如下:
这里有几个注意事项。第一,task集合被转换化为其相应的stream表示。然后,filter操作过滤掉状态为CLOSED的task。下一步,mapToInt操作通过Task::getPoints这种方式调用每个task实例的getPoints方法把Task的stream转化为Integer的stream。最后,用sum函数把所有的分数加起来,得到最终的结果.
在继续讲解下面的例子之前,关于stream有一些需要注意的地方(详情在这里).stream操作被分成了中间操作与最终操作这两种。
中间操作返回一个新的stream对象。中间操作总是采用惰性求值方式,运行一个像filter这样的中间操作实际上没有进行任何过滤,相反它在遍历元素时会产生了一个新的stream对象,这个新的stream对象包含原始stream中符合给定谓词的所有元素。
像forEach、sum这样的最终操作可能直接遍历stream,产生一个结果或副作用。当最终操作执行结束之后,stream管道被认为已经被消耗了,没有可能再被使用了。在大多数情况下,最终操作都是采用及早求值方式,及早完成底层数据源的遍历。
stream另一个有价值的地方是能够原生支持并行处理。让我们来看看这个算task分数和的例子。
这个例子和第一个例子很相似,但这个例子的不同之处在于这个程序是并行运行的,其次使用reduce方法来算最终的结果。
下面是这个例子在控制台的输出:
经常会有这个一个需求:我们需要按照某种准则来对集合中的元素进行分组。Stream也可以处理这样的需求,下面是一个例子:
这个例子的控制台输出如下:
让我们来计算整个集合中每个task分数(或权重)的平均值来结束task的例子。****
下面是这个例子的控制台输出:
最后,就像前面提到的,Stream API不仅仅处理Java集合框架。像从文本文件中逐行读取数据这样典型的I/O操作也很适合用Stream API来处理。下面用一个例子来应证这一点。
对一个stream对象调用onClose方法会返回一个在原有功能基础上新增了关闭功能的stream对象,当对stream对象调用close()方法时,与关闭相关的处理器就会执行。
5.函数式接口(Functional Interface)
所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法。这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。函数式接口里是可以包含默认方法,因为默认方法不是抽象方法,其有一个默认实现,所以是符合函数式接口的定义的;函数式接口里是可以包含静态方法,因为静态方法不能是抽象方法,是一个已经实现了的方法,所以是符合函数式接口的定义的;函数式接口里是可以包含Object里的public方法,这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法);因为任何一个函数式接口的实现,默认都继承了Object类,包含了来自java.lang.Object里对这些抽象方法的实现; 函数式接口里允许子接口继承多个父接口,但每个父接口中都只能存在一个抽象方法,且必须的相同的抽象方法。
/**
* java 8 之前通常使用匿名内部类完成
*/
Collections.sort(dtoList, new Comparator<PlatformCouponOrderDTO>() {
@Override
public int compare(PlatformCouponOrderDTO a, PlatformCouponOrderDTO b) {
return DateUtils.getMinutesBetween(a.getTime(), b.getTime());
}
});
/**
* java 8 之后使用Lambda表达式实现函数式接口,使代码量明显减少许多
*/
Collections.sort(dtoList, (a, b) -> DateUtils.getMinutesBetween(a.getTime(), b.getTime()));
举个栗子:Predicate 和Consumer接口(java.util.function包下的接口)
在Predicate接口中,有以下5个方法:(判断输入的对象是否符合某个条件,返回一个boolean)
@FunctionalInterface
public interface Predicate<T> {
boolean
test(T t);
default Predicate<T>and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
defaultPredicate<T>
negate() {
return (t) -> !test(t);
}
default Predicate<T>or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T>isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
Consumer接口(接受单个输入参数且没有返回值,Consumer接口期望执行带有副作用的操作,即改变输入参数的内部状态)源码实现如下:
@FunctionalInterface
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); };
}
}
我们来考虑一下学生的例子:Student类包含姓名,分数以及待付费用,每个学生可根据分数获得不同程度的费用折扣:
class Student{
String firstName;
String lastName;
Double grade;
Double feeDiscount = 0.0;
Double baseFee = 20000.0;
public Student(String firstName, String lastName, Double grade)
{
this.firstName = firstName;
this.lastName = lastName;
this.grade = grade;
}
public void printFee(){
Double newFee = baseFee - ((baseFee * feeDiscount) / 100);
System.out.println("The fee after discount: " + newFee);
}
}
使用predicate的test方法判断学生满足的折扣条件,Consumer接口的accept方法来更改学生的折扣属性。
public class PreidcateConsumerDemo {
public static Student updateStudentFee(Student student, Predicate<Student> predicate, Consumer<Student> consumer){ //Use the predicate to decide when to update the discount.
if ( predicate.test(student)){
//Use the consumer to update the discount value.
consumer.accept(student);
}
return student;
}
}
updateStudentFee方法的调用如下所示:
public static void main(String[] args) {
Student student1 = new Student("Ashok","Kumar", 9.5);
student1 = updateStudentFee(student1,
//Lambda expression for Predicate interface
student -> student.grade > 8.5,
//Lambda expression for Consumer inerface
student -> student.feeDiscount = 30.0);
student1.printFee();
Student student2 = new Student("Rajat","Verma", 8.0);
student2 = updateStudentFee(student2, student -> student.grade >= 8, student -> student.feeDiscount = 20.0);
student2.printFee();
}
Lambda表达式(也称为闭包)是整个Java 8发行版中最受期待的在Java语言层面上的改变,Lambda允许把函数作为一个方法的参数(函数作为参数传递进方法中),或者把代码看成数据:函数式程序员对这一概念非常熟悉。在JVM平台上的很多语言(Groovy,Scala,……)从一开始就有Lambda,但是Java程序员不得不使用毫无新意的匿名类来代替lambda。
关于Lambda设计的讨论占用了大量的时间与社区的努力。可喜的是,最终找到了一个平衡点,使得可以使用一种即简洁又紧凑的新方式来构造Lambdas。在最简单的形式中,一个lambda可以由用逗号分隔的参数列表、–>符号与函数体三部分表示。例如:
Arrays.asList("a","b","d").forEach(
e -> System.out.println( e ) );
Arrays.asList( "a", "b", "d" ).forEach( e -> {
System.out.print( e );
System.out.print( e );
} );
Lambda可能会返回一个值。返回值的类型也是由编译器推测出来的。如果lambda的函数体只有一行的话,那么没有必要显式使用return语句。下面两个代码片段是等价的:
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> e1.compareTo( e2 ) );
和
Arrays.asList( "a", "b", "d" ).sort( ( e1, e2 ) -> {
int result = e1.compareTo( e2 );
return result;
} );
语言设计者投入了大量精力来思考如何使现有的函数友好地支持lambda。最终采取的方法是:增加函数式接口的概念。函数式接口就是一个具有一个方法的普通接口。像这样的接口,可以被隐式转换为lambda表达式。java.lang.Runnable与java.util.concurrent.Callable是函数式接口最典型的两个例子。在实际使用过程中,函数式接口是容易出错的:如有某个人在接口定义中增加了另一个方法,这时,这个接口就不再是函数式的了,并且编译过程也会失败。为了克服函数式接口的这种脆弱性并且能够明确声明接口作为函数式接口的意图,Java
8增加了一种特殊的注解@FunctionalInterface(Java 8中所有类库的已有接口都添加了@FunctionalInterface注解)。让我们看一下这种函数式接口的定义:
@FunctionalInterface
public interface Functional {
void method();
}
需要记住的一件事是:默认方法与静态方法并不影响函数式接口的契约,可以任意使用:
@FunctionalInterface
public interface FunctionalDefaultMethods {
void method();
default void defaultMethod() {
}
}
2. Default关键字
在Java 8中,接口可以包含带有实现代码的方法,这些方法称为default方法。
3. Optional(见5.函数式接口详述)
到目前为止,臭名昭著的空指针异常是导致Java应用程序失败的最常见原因。以前,为了解决空指针异常,Google公司著名的Guava项目引入了Optional类,Guava通过使用检查空值的方式来防止代码污染,它鼓励程序员写更干净的代码。受到Google
Guava的启发,Optional类已经成为Java 8类库的一部分。
Optional实际上是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。更多详情请参考官方文档。
我们下面用两个小例子来演示如何使用Optional类:一个允许为空值,一个不允许为空值。
1 2 3 4 | Optional< String > fullName = Optional.ofNullable(null ); System.out.println("Full Name is set? " + fullName.isPresent() ); System.out.println("Full Name: " + fullName.orElseGet( () ->"[none]" ) ); System.out.println( fullName.map( s ->"Hey " + s +"!" ).orElse("Hey Stranger!" ) ); |
1 2 3 | Full Name isset?false Full Name: [none] Hey Stranger! |
1 2 3 4 5 | Optional< String > firstName = Optional.of("Tom" ); System.out.println("First Name is set? " + firstName.isPresent() ); System.out.println("First Name: " + firstName.orElseGet( () ->"[none]" ) ); System.out.println( firstName.map( s ->"Hey " + s +"!" ).orElse("Hey Stranger!" ) ); System.out.println(); |
1 2 3 | First Name isset?true First Name: Tom Hey Tom! |
比如以前你从数据库里获取一个对象,然后对他进行操作,可能是这样的代码
1 2 3 4 5 | Object o = dao.find() if(o != null) { doSomething(o) } |
1 2 | Optional<Object> o = dao.find() o.ifPresent(item->doSometing(o)); |
1 2 3 4 5 6 | Object o = dao.find() if(o == null) { throw Exception } return o; |
1 2 | Optional<Object> o = dao.find() return o.orElseThrow(Exception::new) |
1 | return dao.find().orElseThrow(Exception::new); |
optional的正确用法!!!
先又不得不提一下 Optional 的三种构造方式: Optional.of(obj), Optional.ofNullable(obj) 和明确的 Optional.empty()
Optional.of(obj): 它要求传入的 obj 不能是 null 值的, 否则还没开始进入角色就倒在了 NullPointerException 异常上了.
Optional.ofNullable(obj): 它以一种智能的, 宽容的方式来构造一个 Optional 实例. 来者不拒, 传 null 进到就得到 Optional.empty(), 非 null 就调用 Optional.of(obj).
那是不是我们只要用 Optional.ofNullable(obj) 一劳永逸, 以不变应二变的方式来构造 Optional 实例就行了呢? 那也未必, 否则 Optional.of(obj) 何必如此暴露呢, 私有则可?
我本人的观点是: 1. 当我们非常非常的明确将要传给 Optional.of(obj) 的 obj 参数不可能为 null 时, 比如它是一个刚 new 出来的对象(Optional.of(new User(...))), 或者是一个非 null 常量时; 2. 当想为 obj 断言不为 null 时, 即我们想在万一 obj 为 null 立即报告 NullPointException 异常,
立即修改, 而不是隐藏空指针异常时, 我们就应该果断的用 Optional.of(obj) 来构造 Optional 实例, 而不让任何不可预计的 null 值有可乘之机隐身于 Optional 中.
存在即返回, 无则提供默认值
1 2 | return user.orElse(null); //而不是 return user.isPresent() ? user.get() : null; return user.orElse(UNKNOWN_USER); |
1 | return user.orElseGet(() -> fetchAUserFromDatabase());//而不要 return user.isPresent() ? user: fetchAUserFromDatabase(); |
1 2 3 4 5 6 | user.ifPresent(System.out::println); //而不要下边那样 if (user.isPresent()) { System.out.println(user.get()); } |
当 user.isPresent() 为真, 获得它关联的 orders, 为假则返回一个空集合时, 我们用上面的 orElse, orElseGet 方法都乏力时, 那原本就是 map 函数的责任, 我们可以这样一行
1 2 3 4 5 6 7 8 | return user.map(u -> u.getOrders()).orElse(Collections.emptyList()) //上面避免了我们类似 Java 8 之前的做法 if(user.isPresent()) { return user.get().getOrders(); }else { return Collections.emptyList(); } |
1 2 3 | return user.map(u -> u.getUsername()) .map(name -> name.toUpperCase()) .orElse(null); |
1 2 3 4 5 6 7 8 9 10 11 | User user = ..... if(user !=null) { String name = user.getUsername(); if(name != null) { return name.toUpperCase(); }else { returnnull; } }else { returnnull; } |
4.Stream
publicclass Streams { privateenum Status { OPEN, CLOSED }; privatestaticfinalclass Task { privatefinal Status status; privatefinal Integer points; Task(final Status status,final Integer points ) { this.status = status; this.points = points; } public Integer getPoints() { return points; } public Status getStatus() { return status; } @Override public String toString() { return String.format( "[%s, %d]", status, points ); } } } |
1 2 3 4 5 | final Collection< Task > tasks = Arrays.asList( new Task( Status.OPEN, 5 ), new Task( Status.OPEN, 13 ), new Task( Status.CLOSED, 8 ) ); |
1 2 3 4 5 6 7 8 | // Calculate total points of all active tasks using sum() finallong totalPointsOfOpenTasks = tasks .stream() .filter( task -> task.getStatus() == Status.OPEN ) .mapToInt( Task::getPoints ) .sum(); System.out.println("Total points: " + totalPointsOfOpenTasks ); |
1 | Total points: 18 |
在继续讲解下面的例子之前,关于stream有一些需要注意的地方(详情在这里).stream操作被分成了中间操作与最终操作这两种。
中间操作返回一个新的stream对象。中间操作总是采用惰性求值方式,运行一个像filter这样的中间操作实际上没有进行任何过滤,相反它在遍历元素时会产生了一个新的stream对象,这个新的stream对象包含原始stream中符合给定谓词的所有元素。
像forEach、sum这样的最终操作可能直接遍历stream,产生一个结果或副作用。当最终操作执行结束之后,stream管道被认为已经被消耗了,没有可能再被使用了。在大多数情况下,最终操作都是采用及早求值方式,及早完成底层数据源的遍历。
stream另一个有价值的地方是能够原生支持并行处理。让我们来看看这个算task分数和的例子。
1 2 3 4 5 6 7 8 | // Calculate total points of all tasks finaldouble totalPoints = tasks .stream() .parallel() .map( task -> task.getPoints() ) // or map( Task::getPoints ) .reduce( 0, Integer::sum ); System.out.println("Total points (all tasks): " + totalPoints ); |
下面是这个例子在控制台的输出:
1 | Total points (all tasks): 26.0 |
1 2 3 4 5 | // Group tasks by their status final Map< Status, List< Task > > map = tasks .stream() .collect( Collectors.groupingBy( Task::getStatus ) ); System.out.println( map ); |
1 | {CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]} |
1 2 3 4 5 6 7 8 9 10 11 12 | // Calculate the weight of each tasks (as percent of total points) final Collection< String > result = tasks .stream() // Stream< String > .mapToInt( Task::getPoints ) // IntStream .asLongStream() // LongStream .mapToDouble( points -> points / totalPoints ) // DoubleStream .boxed() // Stream< Double > .mapToLong( weigth -> (long )( weigth *100 ) )// LongStream .mapToObj( percentage -> percentage +"%" ) // Stream< String> .collect( Collectors.toList() ); // List< String > System.out.println( result ); |
1 | [19%, 50%, 30%] |
1 2 3 4 | final Path path =new File( filename ).toPath(); try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) { lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println ); } |
5.函数式接口(Functional Interface)
所谓的函数式接口,当然首先是一个接口,然后就是在这个接口里面只能有一个抽象方法。这种类型的接口也称为SAM接口,即Single Abstract Method interfaces。函数式接口里是可以包含默认方法,因为默认方法不是抽象方法,其有一个默认实现,所以是符合函数式接口的定义的;函数式接口里是可以包含静态方法,因为静态方法不能是抽象方法,是一个已经实现了的方法,所以是符合函数式接口的定义的;函数式接口里是可以包含Object里的public方法,这些方法对于函数式接口来说,不被当成是抽象方法(虽然它们是抽象方法);因为任何一个函数式接口的实现,默认都继承了Object类,包含了来自java.lang.Object里对这些抽象方法的实现; 函数式接口里允许子接口继承多个父接口,但每个父接口中都只能存在一个抽象方法,且必须的相同的抽象方法。
/**
* java 8 之前通常使用匿名内部类完成
*/
Collections.sort(dtoList, new Comparator<PlatformCouponOrderDTO>() {
@Override
public int compare(PlatformCouponOrderDTO a, PlatformCouponOrderDTO b) {
return DateUtils.getMinutesBetween(a.getTime(), b.getTime());
}
});
/**
* java 8 之后使用Lambda表达式实现函数式接口,使代码量明显减少许多
*/
Collections.sort(dtoList, (a, b) -> DateUtils.getMinutesBetween(a.getTime(), b.getTime()));
举个栗子:Predicate 和Consumer接口(java.util.function包下的接口)
在Predicate接口中,有以下5个方法:(判断输入的对象是否符合某个条件,返回一个boolean)
@FunctionalInterface
public interface Predicate<T> {
boolean
test(T t);
default Predicate<T>and(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) && other.test(t);
}
defaultPredicate<T>
negate() {
return (t) -> !test(t);
}
default Predicate<T>or(Predicate<? super T> other) {
Objects.requireNonNull(other);
return (t) -> test(t) || other.test(t);
}
static <T> Predicate<T>isEqual(Object targetRef) {
return (null == targetRef)
? Objects::isNull
: object -> targetRef.equals(object);
}
Consumer接口(接受单个输入参数且没有返回值,Consumer接口期望执行带有副作用的操作,即改变输入参数的内部状态)源码实现如下:
@FunctionalInterface
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); };
}
}
我们来考虑一下学生的例子:Student类包含姓名,分数以及待付费用,每个学生可根据分数获得不同程度的费用折扣:
class Student{
String firstName;
String lastName;
Double grade;
Double feeDiscount = 0.0;
Double baseFee = 20000.0;
public Student(String firstName, String lastName, Double grade)
{
this.firstName = firstName;
this.lastName = lastName;
this.grade = grade;
}
public void printFee(){
Double newFee = baseFee - ((baseFee * feeDiscount) / 100);
System.out.println("The fee after discount: " + newFee);
}
}
使用predicate的test方法判断学生满足的折扣条件,Consumer接口的accept方法来更改学生的折扣属性。
public class PreidcateConsumerDemo {
public static Student updateStudentFee(Student student, Predicate<Student> predicate, Consumer<Student> consumer){ //Use the predicate to decide when to update the discount.
if ( predicate.test(student)){
//Use the consumer to update the discount value.
consumer.accept(student);
}
return student;
}
}
updateStudentFee方法的调用如下所示:
public static void main(String[] args) {
Student student1 = new Student("Ashok","Kumar", 9.5);
student1 = updateStudentFee(student1,
//Lambda expression for Predicate interface
student -> student.grade > 8.5,
//Lambda expression for Consumer inerface
student -> student.feeDiscount = 30.0);
student1.printFee();
Student student2 = new Student("Rajat","Verma", 8.0);
student2 = updateStudentFee(student2, student -> student.grade >= 8, student -> student.feeDiscount = 20.0);
student2.printFee();
}
相关文章推荐
- Java 源码解析
- Java 集合系列16之 HashSet详细介绍(源码解析)和使用示例
- java.util.HashMap源码解析
- Java 的抽象特性:抽象类与接口深度解析
- Java Collection Framework 之 ArrayList 源码解析
- linkedlist源码解析(java)
- Java 集合系列13之 WeakHashMap详细介绍(源码解析)和使用示例
- 解析xml的几种方法,他们的原理,比较 以及JAVA源码
- 6种基础排序算法java源码+图文解析
- java.util.logging源码解析
- Java集合, LinkedBlockingQueue源码解析(常用于并发编程)
- Java sort 源码解析 primitive part
- Java集合源码解析(五)HashSet源码解析
- Java-ArrayList源码解析
- 深入解析Java的新特性assertion
- 【Java实战】源码解析为什么覆盖equals方法时总要覆盖hashCode方法
- Java中IO框架——InputStream源码解析
- Java 8新特性探究(二)深入解析默认方法
- Java线程池架构原理和源码解析(ThreadPoolExecutor)
- Java源码解析(附录)(5) —— WildcardType