Java Reference
In-Depth Information
forEach
Terminal Operation
IntStream
static
method
of
(line 14) receives an
int
array as an argument and returns
an
IntStream
for processing the array's values. Once you create a stream, you can
chain
together multiple method calls to create a
stream pipeline
. The statement in lines 14-15
creates an
IntStream
for the
values
array, then uses
IntStream
method
forEach
(a
terminal operation) to perform a task on each stream element. Method
forEach
receives
as its argument an object that implements the
IntConsumer
functional interface (package
java.util.function
)—this is an
int
-specific version of the generic
Consumer
functional
interface. This interface's
accept
method receives one
int
value and performs a task with
it—in this case, displaying the value and a space. Prior to Java SE 8, you'd typically imple-
ment interface
IntConsumer
using an anonymous inner class like:
new
IntConsumer()
{
public void
accept(
int
value)
{
System.out.printf(
"%d "
, value);
}
}
but in Java SE 8, you simply write the lambda
value -> System.out.printf(
"%d "
, value)
The
accept
method's parameter name (
value
) becomes the lambda's parameter, and the
accept
method's body statement becomes the lambda expression's body. As you can see,
the lambda syntax is clearer and more concise than the anonymous inner class.
Type Inference and a Lambda's Target Type
The Java compiler can usually
infer
the types of a lambda's parameters and the type re-
turned by a lambda from the context in which the lambda is used. This is determined by
the lambda's
target type
—the functional interface type that is expected where the lambda
appears in the code. In line 15, the target type is
IntConsumer
. In this case, the lambda
parameter's type is
inferred
to be
int
, because interface
IntConsumer
's accept method ex-
pects to receive an
int
. You can
explicitly
declare the parameter's type, as in:
(
int
value) -> System.out.printf(
"%d "
, value)
When doing so, the lambda's parameter list
must
be enclosed in parentheses. We generally
let the compiler
infer
the lambda parameter's type in our examples.
final
Local Variables, Effectively
final
Local Variables and Capturing Lambdas
Prior to Java SE 8, when implementing an anonymous inner class, you could use local vari-
ables from the enclosing method (known as the
lexical scope
), but you were required to
declare those local variables
final
. Lambdas may also use
final
local variables. In Java SE
8, anonymous inner classes and lambdas can also use
effectively
final
local variables
—that
is, local variables that are
not
modified after they're initially declared and initialized. A lamb-
da that refers to a local variable in the enclosing lexical scope is known as a
capturing lamb-
da
. The compiler captures the local variable's value and ensures that the value can be used
when the lambda
eventually
executes, which may be
after
its lexical scope
no longer exists
.