1- 介绍
从发明JAVA语法到功能升级换代,Java已经通过一些里程碑了:
- Java 1.0: 最开始的程序语言
- Java 1.1, 1.2, 1.3, 1.4: 没有语法和功能很多变化
- Java 1.5 (或Java 5):增加了一些新的概念,并有了一些大的变化。
- 通用/泛型
-
自动装箱/拆箱
-
升级 foreach 功能
-
类型安全的枚举
-
可变参数
-
静态导入
-
元数据
- Java 6,7 在语言中没有大的变化
- Java 8: 语言一个大的变化,增加了一些新的概念和功能:
-
默认的接口方法
-
Lambda表达式
-
引用方法
-
可重复的注解
-
数据流
2- 默认方法接口
Java8让我们通过利用 default 关键字添加非抽象的方法来实现接口。这一特点也称为扩展方法。这是我们的第一个例子:
package com.yiibai.tutorial.j8.itf;
public interface Formula {
// Declare an abstract method.
double calculate(int a);
// Declaring a method is not abstract.
// Use the keyword default.
// (return the square root of a number)
default double sqrt(int a) {
return Math.sqrt(a);
}
}
FormulaImpl 类实现 Formula 接口
package com.yiibai.tutorial.j8.itf;
// Class thi hành Interface Formula
public class FormulaImpl implements Formula {
// implements abstract method
@Override
public double calculate(int a) {
return a*a - a;
}
}
package com.yiibai.tutorial.j8.itf;
public class FormulaTest {
public static void main(String[] args) {
Formula formula = new FormulaImpl();
// ==> 5
double value1 = formula.sqrt(25);
System.out.println("Value1 = " + value1);
// ==> 600
double value2 = formula.calculate(25);
System.out.println("Value2 = " + value2);
}
}
3- 函数接口
Java8认为接口只有一个抽象方法就是函数接口。您可以使用@FunctionalInterface注释,标记你的接口为函数接口。这不是强制性的。但是,如果添加另一个抽象方法插入此注释错误地标记接口,Java编译器会通知显示有错误。
下面是使用 @FunctionalInterface 的一些实际的例子:
下面的例子是一个有效的FunctionalInterface,因为它只有一个抽象方法。
package com.yiibai.tutorial.j8.funcitf;
@FunctionalInterface
public interface Foo {
void something();
default void defaultMethod() {
System.out.println("..");
}
}
无效的例子:
有效的例子:
无效的例子:
有效的例子:
4- Lambda表达式
首先,我们来回顾Java8之前的版本是如何整理集合的。
关于在Java中比较和排序集合,可以看看下面的文章了解更多的细节:
- http://www.yiibai.com/java/comparison-and-sorting-in-java.html
package com.yiibai.tutorial.j8.lambda;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class SortBefore8Example {
public static void main(String[] args) {
// A list of the fruits.
List fruits = Arrays.asList("Grapefruit", "Apple", "Durian",
"Cherry");
// Use of Collections utility method to arrange collection.
// Provide a Comparator.
Collections.sort(fruits, new Comparator() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
});
for (String fruit : fruits) {
System.out.println(fruit);
}
}
}
运行上述示例得到的结果:
Java8知道接口具有唯一的抽象方法,该方法是函数接口。因此,实现接口的时候,只需要编写一个方法来实现唯一的抽象方法。Comparator有一个唯一的抽象方法的接口,并且它是一个函数接口。可以重写上面的例子中 Java8 Lambda 的形式:
package com.yiibai.tutorial.j8.lambda;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
public class SortJava8Example {
public static void main(String[] args) {
// A list of the fruits.
List fruits = Arrays.asList("Grapefruit", "Apple", "Durian",
"Cherry");
// Use of Collections utility method to rearrange the collection.
// Provide a Comparator to the 2nd parameter of the method.
// Comparator has only one abstract method.
// Can write brief with Lambda expressions.
// No need to write the name of the interface,
// no need to write the name of the abstract method.
Collections.sort(fruits, (String o1, String o2) -> {
return o1.compareTo(o2);
});
for (String fruit : fruits) {
System.out.println(fruit);
}
}
}
在语句块中,如果只有一个声明,您可以不使用{},以更简洁的方式来编写代码段。
Collections.sort(fruits, (String o1, String o2) -> o1.compareTo(o2) );
Java编译程序是甚至知道如何确定什么类型来安排的元素,在这个例子中,它是字符串类型。因此比较器 (Comparator) 一定会明确比较字符串类型的数据。也可以更简明地写出。
Collections.sort(fruits, (o1, o2) -> o1.compareTo(o2));
Lambda表达式的其他例子。
package com.yiibai.tutorial.j8.lambda;
@FunctionalInterface
public interface Converter {
T convert(F from);
}
使用Java8之前的版本的格式转换器接口(不使用Lambda)
- ConverterBefore8Example.java
package com.yiibai.tutorial.j8.lambda;
public class ConverterBefore8Example {
public static void main(String[] args) {
// Initialize the Converter object.
Converter converter = new Converter() {
@Override
public Integer convert(String from) {
return Integer.parseInt(from);
}
};
// ==> 100
Integer value = converter.convert("0100");
System.out.println("Value = " + value);
}
}
使用 Java8 的 Lambda 表达式:
- ConveterJava8Example.java
package com.yiibai.tutorial.j8.lambda;
public class ConveterJava8Example {
public static void main(String[] args) {
// Converter is a FunctionalInterface
// Using Java 8 syntax (Lambda)
// In the case of initializing object from FunctionalInterface.
Converter converter1 = (String from) -> {
return Integer.parseInt(from);
};
// ==> 100
Integer value1 = converter1.convert("0100");
System.out.println("Value1 = " + value1);
// Or more simply:
Converter converter2 = (from) -> Integer
.parseInt(from);
// ==> 200
Integer value2 = converter2.convert("00200");
System.out.println("Value2 = " + value2);
// If the method has only one parameter, can ignore ().
Converter converter3 = from -> Integer
.parseInt(from);
// ==> 300
Integer value3 = converter3.convert("00300");
System.out.println("Value3 = " + value3);
}
}
5- 函数接口API
Java8中有准备一个大量的函数接口,这个在 java.util.function 包提供。 在这里,我们将说明如何使用这些接口,这样就可以更容易理解Lambda表达式及其带来的方便。
5.1- java.util.function.Consumer
Consumer是Java8中可用函数接口,它有一个唯一的抽象方法接受一个输入参数,但这个方法无任务内容返回。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Consumer {
// Method to accept an input parameter
// And not return anything.
void accept(T t);
}
使用 List.forEach(Consumer) 方法:
// java.util.List extends java.util.Collection (extends Iterable)
// Interface java.util.Iterable:
default void forEach(Consumer super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
package com.yiibai.tutorial.j8.api;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
public class ConsumerExample {
// Using the method List.forEach (Consumer) with Java <8.
// Print out the elements of the list
public static void beforeJ8() {
List list = Arrays.asList("a", "b", "c", "a1", "a2");
list.forEach(new Consumer() {
@Override
public void accept(String t) {
System.out.println(t);
}
});
}
// Using the method List.forEach(Consumer) with Java 8 syntax.
// (Using lambda expression).
public static void java8Consumer() {
List list = Arrays.asList("a", "b", "c", "a1", "a2");
list.forEach((String t) -> {
System.out.println(t);
});
}
// Using the method List.forEach(Consumer) with Java 8 syntax.
// (Using lambda expression).
// (More simply)
public static void java8ConsumerMoreSimple() {
List list = Arrays.asList("a", "b", "c", "a1", "a2");
list.forEach((String t) -> System.out.println(t));
}
}
5.2- java.util.function.Predicate
Java8可用的谓词函数接口,它有一个唯一的抽象方法接受一个输入参数并方法返回一个布尔值(true/false)。此方法用于计算的输入参数是否适于逻辑。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Predicate {
// Evaluates this predicate on the given argument.
boolean test(T t);
}
在下面的例子中,我们将过滤整数列表,并通过使用Predicate 在 Java8和以前版本的形式来打印奇数的列表。
package com.yiibai.tutorial.j8.api;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Stream;
public class PredicateExample {
// Use the Stream.filter(Predicate ) method way Java <8.
// Filter a list of integers and prints the odd.
public static void beforeJ8() {
List list = Arrays.asList(1, 4, 5, 1, 7, 8);
// Stream containing the elements of the list above.
Stream stream = list.stream();
// A new stream contains odd
Stream stream2 = stream.filter(new Predicate() {
@Override
public boolean test(Integer t) {
return t % 2 == 1;
}
});
}
// Use the Stream.filter (Predicate ) method way Java>= 8.
// Filter a list of integers and prints the odd.
// Using Lambda expressions.
public static void java8Predicate() {
List list = Arrays.asList(1, 4, 5, 1, 7, 8);
// Stream containing the elements of the list above.
Stream stream = list.stream();
// A new stream contains odd
Stream stream2 = stream.filter(t -> {
return t % 2 == 1;
});
// Stream.forEach(Consumer)
stream2.forEach(t -> System.out.println(t));
}
// Use the method Stream.filter (Predicate ) way Java>= 8.
// Filter a list of integers and prints the odd.
// Using Lambda expressions.
// Simple and more concise.
public static void java8ConsumerMoreSimple() {
List list = Arrays.asList(1, 4, 5, 1, 7, 8);
// Stream containing the elements of the list above.
Stream stream = list.stream();
stream.filter(t -> t % 2 == 1).forEach(t -> System.out.println(t));
}
}
5.3- java.util.function.Function
函数就是 Java8 可用函数接口。它有一个唯一的抽象方法接受输入参数,并且方法返回另一个对象。
package java.util.function;
import java.util.Objects;
@FunctionalInterface
public interface Function {
// Applies this function to the given argument.
// return the function result
R apply(T t);
}
例如:给出字符串列表,以大写来打印各个元素
package com.yiibai.tutorial.j8.api;
import java.util.Arrays;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Stream;
public class FunctionExample {
// Use Stream.map(Function) with Java syntax <8.
// Print out the molecules in List.
public static void beforeJ8() {
List list = Arrays.asList("a", "c", "B", "e", "g");
// Stream containing the elements of the list.
Stream stream = list.stream();
// Stream.map(Function):
// Stream map(Function super T, ? extends R> mapper);
// Returns a new Stream, with the elements were changed.
Stream streamUpper = stream.map(new Function() {
@Override
public String apply(String t) {
return t == null ? null : t.toUpperCase();
}
});
streamUpper.forEach(t -> System.out.println(t));
}
public static void java8Function() {
List list = Arrays.asList("a", "c", "B", "e", "g");
// Stream containing the elements of the list.
Stream stream = list.stream();
stream.map(t -> t == null ? null : t.toUpperCase()).forEach(
t -> System.out.println(t));
}
public static void main(String[] args) {
beforeJ8();
java8Function();
}
}
一些相似的函数接口:
- java.util.function.IntFunction
- java.util.function.DoubleFunction
- java.util.function.LongFunction
@FunctionalInterface
public interface IntFunction {
R apply(int value);
}
@FunctionalInterface
public interface LongFunction {
R apply(long value);
}
@FunctionalInterface
public interface DoubleFunction {
R apply(double value);
}
5.4- java.util.function.Supplier
Supplier就是Java8的可用函数接口,具有一个唯一的的抽象方法没有参数,方法返回一个对象。
package java.util.function;
@FunctionalInterface
public interface Supplier {
// Gets a result.
T get();
}
package com.yiibai.tutorial.j8.api;
import java.util.function.Supplier;
public class SupplierExample {
// A method with parameter is Supplier.
public static void display(Supplier supp) {
System.out.println(supp.get());
}
// Not used Lambda.
public static void beforeJ8() {
display(new Supplier() {
@Override
public String get() {
return "Hello";
}
});
display(new Supplier() {
@Override
public String get() {
return "World";
}
});
}
// Using Lambda expressions.
public static void java8Supplier() {
display(() -> {
return "Hello";
});
display(() -> {
return "World";
});
}
// Using Lambda expressions.
// (Write shorter).
public static void java8SupplierShortest() {
display(() -> "Hello");
display(() -> "World");
}
public static void main(String[] args) {
}
}
类似的函数接口:
- java.util.function.BooleanSupplier
- java.util.function.IntSupplier
- java.util.function.DoubleSupplier
- java.util.function.LongSupplier
6- 方法引用
这关系到Lambda表达式的功能。它使我们能够引用构造函数或方法而不执行它们。
方法引用和Lambda相类似的是,它们都需要由一个兼容函数接口的目标类型。
Java8中可以通过关键字::传递的方法和构造函数的引用
看细节之前,让我们来看看一个简单的例子。
myFunction是一个函数接口。它定义了一个方法有两个参数,int a 和 b,并返回整型值。
package com.yiibai.tutorial.j8.mref;
@FunctionalInterface
public interface MyFunction {
// Define a method to do something with a and b
// And returns int.
public int doSomething(int a, int b);
}
MathUtils类有两个静态方法是用来将两个整数相加以及相减。
package com.yiibai.tutorial.j8.mref;
public class MyMathUtils {
// This method has two parameters a, b and returns the int.
// It has a structure similar to MyFunction.doSomething(int,int)
public static int sum(int a, int b) {
return a + b;
}
// This method has two parameters a, b and returns the int.
// It has a structure similar to MyFunction.doSomething
public static int minus(int a, int b) {
return a - b;
}
}
- MethodReferenceExample.java
package com.yiibai.tutorial.j8.mref;
public class MethodReferenceExample {
// The third parameter of this method is MyFunction (A Functional Interface).
// When using this method:
// You can pass the reference of method, if the method is structured
// similar to the abstract method of MyFunction.
public static int action(int a, int b, MyFunction func) {
return func.doSomething(a, b);
}
public static void main(String[] args) {
int a = 100;
int b = 30;
// Pass the reference of MyMathUtils.sum method.
// ==> 130
int c = action(a, b, MyMathUtils::sum);
System.out.println("c = " + c);
// Pass the reference of MyMathUtils.minus method.
// == 70
int d = action(a, b, MyMathUtils::minus);
System.out.println("d = " + d);
// Pass the reference of Math.subtractExact method.
// ==> 70
int e = action(a, b, Math::subtractExact);
System.out.println("e = " + e);
// Pass the reference of Math.min method.
// ==> 30
int f = action(a, b, Math::min);
System.out.println("f = " + f);
}
}