栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Functional Programming in Java venkat(2) hello lambda expressions

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Functional Programming in Java venkat(2) hello lambda expressions

文章目录
  • Functional Programming in Java venkat(2): hello lambda expressions
    • Introduction
    • Hello Lambda Expressions
      • Change the Way You Think
      • The Big Gains of Functional-Style Code
      • Why Code in the Functional Style?
      • Evolution, Not Revolution
      • A Little Sugar to Sweeten
      • Recap
    • 英文
    • 总结
    • 参考

Functional Programming in Java venkat(2): hello lambda expressions Introduction

这里是记录学习这本书 Functional Programming in Java: Harnessing the Power Of Java 8 Lambda Expressions 的读书笔记,如有侵权,请联系删除。

About the author

Venkat Subramaniam

Dr. Venkat Subramaniam, founder of Agile Developer, Inc., has trained and mentored thousands of software developers in the US, Canada, Europe, and Asia. Venkat helps his clients effectively apply and succeed with agile practices on their software projects. He is a frequent invited speaker at international software conferences and user groups. He’s author of .NET Gotchas (O’Reilly), coauthor of the 2007 Jolt Productivity award-winning book Practices of an Agile Developer (Pragmatic Bookshelf),

Hello Lambda Expressions

lambda expression我们只需要显式地告诉它做什么即可,而不用告诉怎么做。

we could then tell it—declaratively—what we want rather than delve into how to do it.

Change the Way You Think

Imperative style—that’s what Java has provided us since its inception. In this
style, we tell Java every step of what we want it to do and then we watch it
faithfully exercise those steps. That’s worked fine, but it’s a bit low level. The
code tends to get verbose, and we often wish the language were a tad more
intelligent; we could then tell it—declaratively—what we want rather than
delve into how to do it. Thankfully, Java can now help us do that. Let’s look
at a few examples to see the benefits and the differences in style.

The Habitual Way

告诉如何去做有点低级(a bit low level)

boolean found = false;
for(String city : cities) {
    if(city.equals("Chicago")) {
    	found = true;
    	break;
	}
}
System.out.println("Found chicago?:" + found);

This imperative version is noisy and low level; it has several moving parts.
We first initialize a smelly boolean flag named found and then walk through each
element in the collection. If we found the city we’re looking for, then we set
the flag and break out of the loop. Finally we print out the result of our finding.

A Better Way

As observant Java programmers, the minute we set our eyes on this code
we’d quickly turn it into something more concise and easier to read, like this:

System.out.println("Found chicago?:" + cities.contains("Chicago"));

That’s one example of declarative style—the contains() method helped us get directly to our business.

lambda expression和函数式编程带来的提升

比如:没有可变的变量,遍历过程被封装不可见 ,less clutter(更少的混乱),清晰度更高,我们只需要关注核心逻辑,较小的阻抗,代码密切跟踪业务意图,更少犯错误,代码容易读并且易于维护。

Tangible Improvements
That change improved our code in quite a few ways:

• No messing around with mutable variables

• Iteration steps wrapped under the hood

• Less clutter

• Better clarity; retains our focus

• Less impedance; code closely trails the business intent

• Less error prone

• Easier to understand and maintain

Beyond Simple Cases

That was simple—the declarative function to check if an element is present
in a collection has been around in Java for a very long time. Now imagine not
having to write imperative code for more advanced operations, like parsing
files, working with databases, making calls to web services, concurrent programming,
and so on. Java now makes it possible to write concise, elegant, less error-prone code, not just for simple cases, but throughout our applications.

The Old Way

Let’s look at another example. We’ll define a collection of prices and try out
a few ways to total discounted price values. Suppose we’re asked to total the prices greater than $20, discounted by 10%.
Let’s do that in the habitual Java way first.

BigDecimal totalOfDiscountedPrices = BigDecimal.ZERO;
for(BigDecimal price : prices) {
    if(price.compareTo(BigDecimal.valueOf(20)) > 0)
    	totalOfDiscountedPrices =
    		totalOfDiscountedPrices.add(price.multiply(BigDecimal.valueOf(0.9)));
}
System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);

That’s familiar code; we start with a mutable variable to hold the total of the
discounted prices. We then loop through the prices, pick each price greater
than $20, compute each item’s discounted value, and add those to the total.
Finally we print the total value of the discounted prices.

上面的写法可以,但是feels dirty。

It worked, but writing it feels dirty. It’s no fault of ours; we had to use what
was available. But the code is fairly low level—it suffers from “primitive
obsession” and defies the single-responsibility principle. Those of us working
from home have to keep this code away from the eyes of kids aspiring to be
programmers, for they may be dismayed and sigh, “That’s what you do for a
living?”

它有效,但写起来感觉很脏。 这不是我们的错; 我们必须使用可用的东西。 但代码相当低级——它受到“原始痴迷”的困扰,违背了单一职责原则。 我们这些在家工作的人必须让这些代码远离那些渴望成为程序员的孩子的眼睛,因为他们可能会感到沮丧和叹息:“这就是你的谋生之道?”

A Better Way, Again

Now we can do better, a lot better. Our code can resemble the requirement
specification. This will help reduce the gap between the business needs and
the code that implements it, further reducing the chances of the requirements
being misinterpreted.

更高层次的抽象

Rather than tell Java to create a mutable variable and then to repeatedly
assign to it, let’s talk with it at a higher level of abstraction, as in the next
code.

final BigDecimal totalOfDiscountedPrices =
  prices.stream()
	.filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0)
	.map(price -> price.multiply(BigDecimal.valueOf(0.9)))
	.reduce(BigDecimal.ZERO, BigDecimal::add);

System.out.println("Total of discounted prices: " + totalOfDiscountedPrices);

代码解释:

Let’s read that aloud—filter prices greater than $20, map the prices to discounted
values, and then add them up. The code flows along with logic in the
same way we’d describe the requirements.

整个流程:首先使用stream方法,其次调用filter和map等匿名函数(就是lambda expression),然后invoke reduce() 来求和。

The code is concise, but we’re using quite a number of new things from Java8

First, we invoked a stream() method on the prices list. This opens the door to
a special iterator with a wealth of convenience functions, which we’ll discuss
later.

Instead of explicitly iterating through the prices list, we’re using a few special
methods, such as filter() and map(). Unlike the methods we’re used to in Java
and the Java Development Kit (JDK), these methods take an anonymous
function—a lambda expression—as a parameter, within the parentheses ().
(We’ll soon explore this further.) We invoke the reduce() method to compute
the total on the result of the map() method.

The looping is concealed much like it was under the contains() method. The
map() method (and the filter() method), however, is more sophisticated. For each
price in the prices list, it invokes the provided lambda expression and puts the
responses from these calls into a new collection. The reduce() method is invoked
on this collection to get the final result.

习惯方面的提升:比如,很好的排列组合在一起,没有混乱感;没有低阶操作;更容易修改代码逻辑;迭代不用显式进行更安全;更高效,lazy求值;更容易并行化处理。

The Improvements

This is quite an improvement from the habitual way:
• Nicely composed, not cluttered

• Free of low-level operations

• Easier to enhance or change the logic

• Iteration controlled by a library of methods

• Efficient; lazy evaluation of loops

• Easier to parallelize where desired

Lambdas to the Rescue

lambda表达式的好处

Lambdas are the functional key to free us from the hassles of imperative
programming. By changing the way we program, with a feature now baked
into Java, we can write code that’s not only elegant and concise, but also less
prone to errors; more efficient; and easier to optimize, enhance, and parallelize.

函数式编程的巨大好处

The Big Gains of Functional-Style Code

信噪比高:更少的代码,实现同样的功能

Functional-style code has a higher signal-to-noise ratio; we write fewer lines
of code, but each line or expression achieves more. We gained quite a bit from
the functional-style version, compared with the imperative-style version:

具体的gains

避免了显式的更改 avoided explicit mutation,就会少犯错。

• We avoided explicit mutation or reassignment of variables, which are often
sources of bugs and make it hard to keep the code concurrent. In the
imperative version we repeatedly set the totalOfDiscountedPrices variable
within the loop. In the functional-style version, there is no explicit mutation
in our code. Fewer mutations leads to fewer errors in code.

函数式编程更容易并行化,不用担心线性安全问题。

• The functional version can easily be parallelized. If the computation was
time consuming, we can easily run it concurrently for each element in
the list. If we parallelized the imperative version, we’d have to worry about
concurrent modification of the totalOfDiscountedPrices variable. In the functional
version we gain access to the variable only after it’s fully baked,
which removes the thread-safety concerns.

代码可读性更好

• The code is more expressive. Rather than conveying the intent in multiple
steps—create an initial dummy value, loop through prices, add discounted
values to the variable, and so on—we simply ask the list’s map() method
to return another list with discounted values, and summed it.

代码更concise

• The functional-style version is more concise; it took us fewer lines of code
to achieve the same result as the imperative version. More concise code
means less code to write, less code to read, and less code to maintain—
see Does concise just mean less code?, on page 7.

代码更加符合直觉:我们思考的方式

• The functional-style version is intuitive—code reads more like how we’d
state the problem—and it’s easier to understand once we’re familiar with
the syntax. The map() method applies the given function (which computes
the discount) to each element of the collection and produces the resulting
collection, as we see in the following figure.

图片来源:functional programming in java venkat

正是由于lambda 表达式的出现,java才得以完全利用函数式编程的好处。

With the support for lambda expressions, we can fully utilize the power of
the functional style of programming in Java. If we embrace this style, we can
create more expressive, more concise code with less mutability and fewer
errors.

java8 可以利用面向对象和函数式编程。

One of Java’s key strengths has been its support of the object-oriented (OO)
paradigm. The functional style is not counter to OO. The real paradigm shift
is from the imperative to the declarative style of programming. With Java 8,
we can now intermix functional and OO styles of programming quite effectively.
We can continue to use the OO style to model domain entities, their state,
and relationships. In addition, we can model the behavior or state transformation,
business workflows, and data processing as a series of function composition.

Why Code in the Functional Style?

Is it worth picking up the functional style? Now let’s answer the following questions.

We saw the general benefits of the functional style of programming, but is it
worth picking up this new style? Should we expect a marginal improvement,
or is it life altering? Those are genuine questions that we need answered
before we commit our time and effort.

之前的java,开发很简单,但是难在代码维护,尤其是企业级的应用。

Writing Java code is not that hard; the syntax is simple. We’ve gotten quite
familiar and comfortable with the libraries and their application programming
interfaces (APIs). What really gets us is the effort required to code and maintain
the typical enterprise applications we use Java to develop.

我们必须确保其他程序员在正确的时间关闭了数据库连接,他们不会持续超过需要的事务,他们在正确的级别上很好地处理异常,他们正在保护和 正确地释放锁……这样的例子还在继续。

We must ensure that fellow programmers have closed the database connections
at the right time, that they’re not holding on to transactions any longer
than needed, that they’re handling the exceptions well and at the right level,
that they’re securing and releasing locks properly…and the list goes on.

上面的单个问题看起来并不是很难处理,当它们一起出现的时候,就很费力。

Each one of these in isolation may not seem like a big deal. But when combined
with the domain’s inherent complexities, things get quite overwhelming,
labor intensive, and hard to maintain.

把小决策都封装成可以管理约束的小段代码

What if we could encapsulate each of these decisions into tiny pieces of code
that can manage the constraints well? Then we wouldn’t have to continuously
expend energy to enforce policies. That would be a big win, so let’s see how
the functional style can help.

Iteration on Steroids 这里的Steroids是什么意思? 也有分解的意思吧,就是说把各种遍历给拆解啦。

steroid的本意是:类固醇

遍历其实很难。

We write iterations all the time to process a list of objects and to work with
sets and maps. The iterators we’re used to in Java are familiar and primitive,
but not simple. Not only do they take a few lines of code to work with; they’re
also quite hard to compose.

很多的可变变量 mutable variables

How do we iterate and print each element in a collection? We could use a for
loop. How do we select some elements from a collection? With the same for
loop, but some extra mutable variables have to step in to support the operation.
Now after selecting the values, how do we reduce the operations into a
single value, like the min, the max, or an average? More looping, more
mutable variables.

现在出场的是:很多专有的内部迭代器,比如对loop的迭代器,专门用来map的迭代器,专门用来select value的迭代器等等,还有一些容易组合使用的方法。

That’s like having a jack-of-all-iterations, but a master of none. Java now
provides specialized internal iterators for various operations: one to simply
loop, one to map data values, one to filter out select values, one to reduce,
and several convenience functions to pick the min, the max, the average, and so on. In addition, these operations nicely compose so we can combine a variety of them to implement the business logic with greater ease and less
code. When we’re done, the code is easier to understand, as it logically flows
thought the sequence described in the problem.

Enforcing Policies

Policies rule enterprise applications. For instance, we may have to ensure an
operation has proper security credentials. We may have to ensure that
transactions run fast and update audit trails properly. These tasks often turn
into mundane service-tier code like the following pseudocode form:

Transaction transaction = getFromTransactionFactory();
//... operation to run within the transaction ...

checkProgressAndCommitOrRollbackTransaction();
UpdateAuditTrail();

Two issues with abovementioned approach:

  • increase maintenance cost
  • easy to forget about exceptions

There are two issues with this kind of approach. First, it often leads to
duplication of effort and, in turn, increases maintenance cost. Second, it’s
easy to forget about exceptions that may be thrown in the application code,
thus jeopardizing the transaction lifetime and the update of audit trails. We
could implement a proper try-finally block, but every time someone touches
that code, we’d have to reverify that it’s not broken.

A better way to do it

Alternatively, we could get rid of the factory and turn this code on its head.
Instead of receiving a transaction, we could send the processing code to a
well-managed function, like so (in pseudocode):

runWithinTransaction((Transaction transaction) -> {
//... operation to run within the transaction ...
});

Benefits: no longer worry about forgetting to perform some steps or about the exceptions being handled well.

This is such a small step with a huge savings. The policy to check the status
and update the audit trails is abstracted and encapsulated within the runWithinTransaction()
method. To this method we send a piece of code that needs to
run in the context of a transaction. We no longer have to worry about forgetting
to perform the steps or about the exceptions being handled well. The policyenforcing
function takes care of all that.

Extending Policies

It’s a common task that polices need to be extended.

Policies seem to grow around us—beyond their being enforced, enterprise
applications require ways to extend them. Based on some configuration
information we may have to add or remove a series of operations that, in turn,
may have to be processed before core logic in a module is executed. This is
a common task in Java, but it requires much forethought and design.

Design more interfaces to extend, however, design can be heavyweight.

The machinery for extensibility is often one or more interfaces. We could
carefully design these interfaces and the hierarchy of classes that will implement
them. The result may be effective, but this effort possibly leaves a
number of interfaces and classes that we have to maintain. The design can
easily become heavyweight and hard to maintain, jeopardizing the very goal
of extensibility we set out for.

A better way: functional interfaces and lambda expressions.

There’s an alternative—functional interfaces and lambda expressions, which
let us design extensible policies. This way we’re not forced to create extra
interfaces or conform to a method name, but instead we can focus on the
core behaviors we’d like to provide, as we’ll see in Decorating Using Lambda
Expressions, on page 73.

Hassle-Free Concurrency

性能瓶颈

A big application is close to its delivery milestone when a huge performance
issue comes to the surface. The team quickly figures out that the bottleneck
is in the titanic module of the application, which involves processing large
volumes of data. Someone on the team suggests that we can improve performance
if we more effectively exploit the available multiple cores. But the
excitement from the suggestion is likely short lived if the titanic module is
like typical old-style Java code.

old-style java code想要转型并发很难。

The team quickly realizes that converting the titanic module’s code from a
sequential to a concurrent version would take substantial effort, create
additional complexity, and open doors for many multithreading-related bugs.
Isn’t there an easier way to get better performance?

现在工具出来了,并发和串行执行在代码上没有任何区别,只是需要一个开关!

What if there is no difference between sequential and concurrent code, and
the effort to run it sequentially versus concurrently is merely the flip of a
switch to clearly express our intent?

使用函数式style就可以实现之。

That may seem possible only in Narnia, but it’s quite real if we develop our
modules with functional purity. The internal iterators and functional style
remove the last roadblock to easy parallelism. The JDK library has been
designed to make the switch between serial and parallel execution require only a small and explicit but unobtrusive code change, as we’ll see in Taking
a Leap to Parallelize, on page 144.

Telling the Story

商业需求和代码实现之间总是存在误差。如果代码读起来和问题陈述一样会怎样呢?

So much is lost in the translation between what the business wants and how
the code implements it. The bigger that gap, the greater the chance of errors
and higher the cost of maintenance. If the code reads more like the way the
business states the problem, it becomes easier to read, easier to discuss with
the business folks, and easier to evolve to meet their changing demands.

举例

For instance, you hear the business say, “Get the prices for all the tickers,
find the prices that are less than $500, and total the net asset value of only
the stocks that make the cut.” Using the new facilities available, we can write
something like this:

tickers.map(StockUtil::getprice).filter(StockUtil::priceIsLessThan500).sum()

上述代码就没有丢失客户的需求,就是按照业务逻辑做的。

There’s little chance of losing something in translation here, as there’s not
much to translate. This is function composition at work, and we’ll see more
of it in this book, especially in Chapter 8, Composing with Lambda Expressions,
on page 137.

Separation of Concerns

应用程序中的一个常见需求是将核心计算与其所依赖的细粒度逻辑分离。

A common need in applications is the separation of the core computations
from the fine-grained logic it depends on. For example, an order-processing
system may want to apply different tax computations based on the origin of
transaction. Separating the tax-computation logic from the rest of the processing
will help us create more reusable and extensible code.

在面向对象的思考框架中,我们尝试创建更多的接口和类来实现之。

In OO programming we call this separation of concern and often use the
strategy pattern to solve it. The effort typically involves creating one or more
interfaces and a bunch of classes to implement it.

现在引入函数式编程让这件事更简单,我们会在后面的章节看到。

We can achieve the same now, but with far less code. And we can try out our
design ideas really fast without being bogged down by a hierarchy of code
that we have to lay out first. We’ll cover how to create this pattern and separate
concerns using lightweight functions in Separating Concerns Using Lambda
Expressions, on page 63.

Delaying Evaluation

不是所有东西都是急需处理的,而是可以延后的。

When creating enterprise applications, we may have to interact with web
services, make database calls, process XML…the list goes on. There are so
many operations that we have to perform, but not all of them are necessary all the time. Avoiding some operations or at least postponing the ones that don’t have to be performed yet is one of the easiest ways to improve performance
and application start-up or response time.

单纯的OO方法不好处理

It’s a simple goal, but one that may be quite hard to implement using a pure
OO approach. We would have to fuss with object references and null checks
to postpone initialization of heavyweight objects, for instance.

好的方法:使用新的Optional 类和函数式接口

Alternatively, we can minimize our effort and make the intent more explicit
by using the new Optional class and the functional-style API it provides, as we’ll
see in Delayed Initialization, on page 105.

Improving Testability

函数式的moving parts比较少,更安全,需要的测试就少。

Fewer things tend to break in code that has few moving parts. By nature,
functional-style code is more resilient to change and requires relatively less
testing effort.

使用mocks和stubs可以进行可读性更好的异常测试。lambda表达式让测试更简单。

In addition, as we’ll see in Chapter 4, Designing with Lambda Expressions,
on page 63, and Chapter 5, Working with Resources, on page 89, lambda
expressions can stand in as lightweight mocks or stubs, and can help create
highly expressive exception tests. Lambda expressions can also serve as a
great testing aid. A common set of test cases can receive and exercise lambda
expressions. The tests can capture the essence of behaviors that need to be
tested for regression. At the same time, the lambda expressions being passed
in can serve as variations of implementations that need to be exercised.

JDK中有很多自动化测试的例子

The automated tests that are part of the JDK itself are great examples of this
—for more details browse through the JDK source code in the OpenJDK
repository. These tests show how lambda expressions help parameterize the
test cases’ key behaviors; for example, they help compose the tests as “make
a container for the results” followed by “assert some parameterized postconditions.”

我们讨论了函数式风格带来的众多好处。

We’ve discussed how the functional style not only helps us write better-quality
code, but also solves elegantly so many of our common application-development
challenges. That means we can create applications more quickly,
with less effort and fewer errors—as long as we follow a few guidelines, as
we’ll discuss next.

Evolution, Not Revolution

进化,而不是革命

To reap the benefits of functional style, we don’t have to switch over to
another language; we simply have to change the way we use Java.

现在的很多语言,比如CPP,Java,C#都在拥抱函数式特性。

Languages like C++, Java, and C# started out with support for imperative
and OO programming. Now all these languages also embrace the functional
style of programming. We just saw examples of these two styles and discussed
the benefits we derived from the functional style. Now let’s look into some key
concepts and practices that will help us adopt the new style.

下面有5个方面学习,如果我们想充分发挥java8中函数式编程的威力的话。

The Java language team has put in substantial time and effort to bring
functional capabilities to the language and the JDK. To reap the benefits, we
have to pick up a few new concepts. We can improve our code if we follow a
few guidelines:

• Be declarative.

• Promote immutability.

• Avoid side effects.

• Prefer expressions over statements.

• Design with higher-order functions.

Be Declarative

命令式风格:带来很多mutability,而且需要写很多显式的命令来指导指令执行。

Imperative style: mutability, modifying states of variables along the way.

At the core of the familiar imperative style are mutability and command-driven
programming. We create variables or objects and modify their state
along the way. We also provide detailed commands or instructions to execute,
such as create a loop index, increment its value, check if we reached the end,
update the nth element of an array, and so on. It made sense for us to program
this way in the past due to the nature of the tools and the hardware limitations.

Declarative风格:immutability, 低层次的细节都被封装到库函数之中。

不变性和声明式编程是函数式编程的核心:Immutability and declarative programming are the essence of the functional style of programming

We saw how the declarative use of the contains() method—when used on an
immutable collection—was far easier to work with than the imperative style.
All the hard work and the lower-level details were moved into the library
function and we don’t have to deal with those details. We would prefer doing
everything this way if it were only easier. Immutability and declarative programming
are the essence of the functional style of programming, and Java
now makes them quite approachable.

Promote Immutability

多变量改变的代码不好读,而且很难并行化处理。

Mutable code has many moving parts. The more things change, the easier it
is for components to break and for errors to creep in. Code where multiple
variables change is hard to understand and quite difficult to parallelize.
Immutability removes all these problems at the root.

我们java8 开发者必须改变使用mutating object’s states的习惯,相反,要使用immutable objects

Java supports immutability but does not enforce it—but we can. We need to
change our old habits of mutating objects’ states. As much as possible, we
must use immutable objects.

尽量把variables,fields和parameters 声明为final。

When declaring variables, fields, and parameters, lean toward declaring them
final, following the sage advice “Treat objects as immutable” from Effective
Java [Blo08], by Joshua Bloch et al.

举例

When creating objects, promote immutable objects such as the String class.
When working with collections, create immutable or unmodifiable collections
using functions like Arrays.asList() or the Collections class’s unmodifiableList() method,
for example.

什么叫做pure function? 就是没有mutability的函数,不会带来任何副作用。

By avoiding mutability we can create pure functions—that is, functions with
no side effects.

Avoid Side Effects

当有很多mutable的变量、并行化的时候, 需要花费时间来处理线程同步的问题,很麻烦。

Imagine writing a piece of code to go out to the Web to fetch a stock price and
then update a shared variable. If we have a number of prices to fetch, we’re
forced to run these time-consuming operations sequentially. If we resort to
multithreading, then we have to burden ourselves with threading and synchronization
issues to prevent race conditions. The net result is poor application
performance and/or lack of sleep trying to manage multiple threads. We
can totally eliminate the problems by removing the side effect.

没有副作用的函数很容易并行化,不用担心对其他方法、类带来影响,这就大大提升了性能。

A function with no side effects honors immutability and does not change its
input or anything in its reach. These functions are easier to understand, have
fewer errors, and are easier to optimize. The lack of side effects removes any
concerns of race conditions or simultaneous updates. As a result we can also
easily parallelize execution of such functions, as we’ll see in Taking a Leap to
Parallelize, on page 144.

Prefer Expressions Over Statements

更喜欢expressions(表达式),不喜欢statements(声明,很多改变状态的表达式)

statement 比如赋值表达式,++,–,方法调用等,都是改变状态的。

Statements are stubborn and force mutation. Expressions promote
immutability and function composition. For example, we first used the for
statement to compute the total of discounted prices. This version promoted mutation and verbose code. By switching over to the more expressive declarative version using the map() and sum() methods, which are expressions, we
avoided mutations and were able to chain or compose functions.

多用expressions,这样我们的code会更concise。

It’s better to design with expressions and use them more than statements in
our code. This will make the code concise and easier to understand. The
code will flow logically, in the same order in which we would state the problem.
The concise version is easier to change if the problem changes.

补充什么是statement

https://docs.oracle.com/javase/tutorial/java/nutsandbolts/expressions.html

Statements

Statements are roughly equivalent to sentences in natural languages. A statement forms a complete unit of execution. The following types of expressions can be made into a statement by terminating the expression with a semicolon (;).

  • Assignment expressions
  • Any use of ++ or --
  • Method invocations
  • Object creation expressions

Such statements are called expression statements. Here are some examples of expression statements.

// assignment statement
aValue = 8933.234;
// increment statement
aValue++;
// method invocation statement
System.out.println("Hello World!");
// object creation statement
Bicycle myBike = new Bicycle();

In addition to expression statements, there are two other kinds of statements: declaration statements and control flow statements. A declaration statement declares a variable. You’ve seen many examples of declaration statements already:

// declaration statement
double aValue = 8933.234;

Design with Higher-Order Functions

java不是纯的函数式语言,比如Haskell,但是我们可以使用函数式的风格来写java code, 通过高阶函数。

Unlike some functional programming languages, such as Haskell, that enforce
immutability, Java lets us modify variables at will. In that regard Java is not,
and will never be, a pure functional programming language. However, we can
write code in the functional style in Java by using higher-order functions.

高阶函数的概念可以让我们重复使用很多small, focused, cohesive, and well-written functions.

A higher-order function takes the concept of reuse to the next level. Instead
of solely relying on objects and classes to promote reuse, with higher-order
functions we can easily reuse small, focused, cohesive, and well-written
functions.

高阶函数的特性:把函数传递给函数,在函数中创建函数,从函数中返回函数。就像对象在面向对象中的那样。

In OO programming we’re used to passing objects to methods, creating objects
within methods, and returning objects from within methods. Higher-order
functions do to functions what methods did to objects. With higher-order
functions we can

• Pass functions to functions

• Create functions within functions

• Return functions from functions

下面是把函数传递给函数的实例:

We already saw an example of passing a function to another function, and
we’ll see examples of creating and returning functions later. Let’s look at our
“passing a function to a function” example again.

prices.stream()
	.filter(price -> price.compareTo(BigDecimal.valueOf(20)) > 0)
	.map(price -> price.multiply(BigDecimal.valueOf(0.9)))
	.reduce(BigDecimal.ZERO, BigDecimal::add);

对上面的代码进行解释:匿名函数(没有名字, anonymous),我们更愿意称之为lambda 表达式。

In this example we’re sending a function, price -> price.multiply(BigDecimal.valueOf(0.9)),
as an argument to map. The function being passed is created just in time, at
the point of call to the higher-order function map. Generally a function has a
body, a name, a parameter list, and a return type. The just-in-time function
created here has a parameter list followed by an arrow (->), and then the short
body. The type of the parameter may be inferred by the Java compiler here and the return type is implicit. This function is anonymous; it has no name.
Rather than referring to them as anonymous functions, we call them lambda
expressions.

传递匿名函数在 Java 中并不是一个完全陌生的概念。 我们习惯于传递匿名类的实例。 如果我们的匿名类只有一个方法,我们仍然必须经历创建一个类的过程,尽管是匿名的,然后实例化它。 相反,我们现在可以使用 lambda 表达式享受 Java 中的轻量级语法。 此外,我们习惯于用对象抽象概念。 现在我们可以将它与使用 lambda 表达式的抽象行为结合起来。

Passing anonymous functions is not a totally unknown concept in Java; we’re
used to passing instances of anonymous classes. If our anonymous class had
only one method, we still had to go through the ceremony of creating a class,
albeit anonymous, and instantiating it. Instead we can now enjoy a lightweight
syntax in Java with lambda expressions. Additionally, we’re accustomed to
abstracting concepts with objects. Now we can combine that with abstracting
behavior using lambda expressions.

需要好好学functional style

It takes some rethinking to design applications with this style of programming.
We have to tune our imperative-ingrained minds to think functionally. This
may seem a bit difficult at the beginning, but we’ll get used to it in no time,
and can leave those dysfunctional APIs far behind as we move forward.

下面开始看看java到底是如何把函数作为参数传递的。

Let’s now switch gears and look at how Java handles lambda expressions.
We’re used to passing objects to methods, but now we can store functions
and pass them around. Let’s look at the magic behind how Java accepts a
function as an argument.

A Little Sugar to Sweeten

lambda表达式提供了语法糖,剔除了那些繁文缛节。

Lambda expressions remove the ceremony and sweeten our efforts by adding a little syntax sugar.

We could implement all the ideas with what was already available in Java,
but lambda expressions remove the ceremony and sweeten our efforts by
adding a little syntax sugar. This quickly translates into code that’s faster to
create and makes it easier to express our ideas.

单方法的接口现在很好用

In the past we’ve used a number of interfaces that only have single methods:
Runnable, Callable, Comparable, and so on. These interfaces are common in the
JDK library and often appear where just a single function is expected. All
these existing library methods that expect a single method interface can now
accept lightweight functions, thanks to the brilliant syntax sugar provided
through functional interfaces.

函数式接口就是拥有一个未实现的方法的接口。

函数式接口可以拥有默认方法。

A functional interface is an interface with one abstract—unimplemented—
method. Again think single-method interfaces like Runnable, Callable, Comparable,
and so on, which all fit that definition. JDK 8 has more of these types of
interfaces—Function, Predicate, Consumer, Supplier, and so on (for a summary of the
starter set of functional interfaces see Appendix 1, Starter Set of Functional
Interfaces, on page 157). A functional interface may also have zero or more
default methods, which are implemented right within the interface.

可以使用注解在标注一个函数式接口

We can mark a functional interface with the @FunctionalInterface annotation. The
compiler does not require this annotation, but it is helpful to explicitly state
the purpose that the interface serves. Furthermore, if we mark an interface
with this annotation, the compiler will enforce the rules for the interface to
qualify as a functional interface.

如果一个方法可以把函数式接口作为参数,那么我们就可以传:匿名内部类(但是没有必要),lambda表达式,方法引用或者构造器引用。

If a method takes a functional interface as a parameter, then we can pass
the following:

• An anonymous inner class, the old-fashioned way (but why would we?)

• A lambda expression, like we did when we called the map() method

• A method or constructor reference (as we’ll see later)

编译器会很快处理好的。

The compiler readily accepts a lambda expression or a method/constructor
reference as an argument if the method’s corresponding parameter is a reference
to a functional interface.

当我们将 lambda 表达式传递给方法时,编译器会将 lambda 表达式转换为适当的函数接口的实例。 这种转换不仅仅是内部类的生成。 该实例的synthesized方法符合参数对应的功能接口的抽象方法。 例如,map() 方法将函数接口 Function 作为其参数。 在对 map() 方法的调用中,Java 编译器对其进行综合(synthesized),如下图所示。

When we pass a lambda expression to a method, the compiler will convert
the lambda expression to an instance of the appropriate functional interface.
This conversion is not a mere generation of an inner class in place. The synthesized
method of this instance conforms to the abstract method of the functional
interface that corresponds to the argument. For example, the map()
method takes the functional interface Function as its parameter. In a call
to the map() method, the Java compiler synthesizes it, as the following figure
shows.

图片来源:Functional Programming in Java: Harnessing the Power Of Java 8 Lambda Expressions 1st Edition

lambda表达式的参数必须和接口的抽象方法的参数匹配。 综合方法的返回值如果和抽象方法不同,会进行返回值类型转换。

The parameters of the lambda expression must match the parameters of the
interface’s abstract method. This synthesized method returns the lambda
expression’s result. If the return type doesn’t directly match that of the abstract
method, the synthesized method may convert the return value to a proper
assignable type.

上面,我们大概熟悉了一下lambda表达式是如何传递给方法的。

We took a peek at how lambda expressions are passed as arguments to
methods. Let’s quickly review what we covered and move on to explore
lambda expressions.

Recap

The java compiler works its magic! Lambda表达式的使用还是得归功于编译器。

It’s a whole new world in Java. We can now program in an elegant and fluent
functional style, with higher-order functions. This can lead to concise code
that has fewer errors and is easier to understand, maintain, and parallelize.
The Java compiler works its magic so we can send lambda expressions or
method references where functional interfaces are expected.

准备好深入了解 lambda 表达式的有趣部分,以及经过微调以使用 lambda 表达式的 JDK 库。

We’re all set to dive into the fun parts of lambda expressions and the JDK
library that’s been fine-tuned to work with lambda expressions. In the next
chapter we’ll start by using lambda expressions in one of the most fundamental
programming tasks: working with collections.

英文

resemble the requirement specification: 符合要求规范

hassle:ˈhasəl 麻烦, 举例: keep us from the hassles 让我们免于麻烦

fine-grained: 细颗粒度,细粒度

bog 沼泽

bog down:停滞

being bogged down: 陷入困境

fuss:瞎忙;大惊小怪

We would have to fuss with object references and null checks to postpone initialization of heavyweight objects, for instance.

我们将不得不瞎忙于对象引用和空检查, 例如,推迟重量级对象的初始化。

dysfunctional: ˌdisˈfəNG(k)SH(ə)nl 失调的 not operating normally or properly.

the telephones are dysfunctional

switch gears: 换挡

Let’s now switch gears and look at how Java handles lambda expressions.

parallelize: ˈparələˌlīz 重音在前

take a peek at: 看了一眼

这句表达写的真好:

lambda expressions remove the ceremony and sweeten our efforts by adding a little syntax sugar.

不愧是java领域最好的演讲者之一。

总结

这一章,学习了java8 引入lambda expression的众多好处和优点,解决了为什么引入的问题。

另外,将这篇笔记发布出来,需要重新整理,再次梳理,起码学习到两点。

第一,世界上最好的那批人是如何思考的,是怎么写文章的。

第二,让我对Java产生了更大的兴趣,以及一些JVM语言,比如Scala,Kotlin,Groovy。

下面是Venkat写的一些技术书籍。现在在读的有三四本,希望有机会一一拜读。

来源:google

参考

Functional Programming in Java: Harnessing the Power Of Java 8 Lambda Expressions 1st Edition:https://www.amazon.com/Functional-Programming-Java-Harnessing-Expressions/dp/1937785467

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/851939.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号