java 8 新特性
java 8 新特性
Lambda 表达式
lambda表达式用于简化匿名内部类的实现,只作用于接口,抽象方法无法使用。
- Lambda 作用于函数式接口(有且仅有一个抽象方法,但是可以有多个非抽象方法的接口)。
- Lambda 表达式产生函数,而不是类。
- Lambda 语法尽可能少,这正是为了使 Lambda 易于编写和使用。
interface Description { String brief(); } public class LambdaTest { @Test public void test() { // 旧版实现 Description d1 = new Description() { @Override public String brief() { return "brief1"; } }; System.out.println(d1.brief()); // lambda 实现 Description d2 = () -> "brief2"; System.out.println(d2.brief()); } }
语法格式
任何 Lambda 表达式的基本语法是:
(parameters) -> { statements; }
- 参数(parameters),接口中的抽象方法的实参列表。
- 接着
->
,lambda 操作符或箭头函数。 ->
之后的内容都是方法体{ statements; }。
- 当只用一个实参,可以不需要括号
()
。 - 正常情况使用括号
()
包裹实参。 - 如果没有参数,则必须使用括号
()
表示空参数列表。 - 对于多个参数,将实参列表放在括号
()
中。
interface Description { String brief(); } interface Body { String detailed(String head); } interface Multi { String twoArg(String head, Double d); } public class LambdaExpressions { static Body bod = h -> h + " No Parens!"; static Body bod2 = (h) -> h + " More details"; static Description desc = () -> "Short info"; static Multi mult = (h, n) -> h + n; static Description moreLines = () -> { System.out.println("moreLines()"); return "from moreLines()"; }; public static void main(String[] args) { System.out.println(bod.detailed("Oh!")); System.out.println(bod2.detailed("Hi!")); System.out.println(desc.brief()); System.out.println(mult.twoArg("Pi! ", 3.14159)); System.out.println(moreLines.brief()); } }
Lambda 表达式方法体是单行时该表达式的结果自动成返回值,在此处使用 return 关键字是非法的。 这是 Lambda 表达式简化相应语法的另一种方式。
如果在 Lambda 表达式中需要多行,则必须将这些行放在花括号中。 在这种情况下,就需要使用 return。
函数式接口
函数式接口 (Functional Interface) 就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。
@FunctionalInterface
: 注解只是在编译时起到强制规范定义的作用(可省略,省略后不做规范检查)。
函数式接口可以被隐式转换为 lambda 表达式。
Java 内置四大核心接口:
Consumer<T> : 消费型接口,void accept (T t);
:表示接受参数并且不返回结果的操作。
Supplier<T> : 供给型接口,T get ();
:表示返回类型 T
的值的函数。
Function<T, R> : 函数型接口,R apply (T t);
:表示接受类型 T
的参数并返回类型 R
的结果的函数。
Predicate<T> : 断言型接口,boolean test (T t);
:表示为指定参数返回 true
或 false
的布尔函数。
简单示例:
public class LambdaTest { public void filterString(List<String> strings, Predicate<String> predicate) { for (String string : strings) { System.out.println(predicate.test(string)); } } public void queryString(List<String> strings, Function<String, Boolean> function) { for (String string : strings) { System.out.println(function.apply(string)); } } @Test public void test() { List<String> strings = Arrays.asList("你好", "我好", "大家好"); // 打印List信息 Consumer<List<String>> consumer = s -> System.out.println("List:" + s); consumer.accept(strings); // 返回一个 String Supplier<String> stringSupplier = () -> "Supplier<T> : 供给型接口,T get ();"; System.out.println(stringSupplier.get()); // 查找字符串中是否包含 “家” 字符 Function<String, Boolean> function = s -> s.contains("家"); queryString(strings, function); // 查找字符串中是否包含 “你” 字符 filterString(strings, s -> s.contains("你")); } }
在编写自定义接口时,可以使用 @FunctionalInterface
注解强制执行此“函数式方法”模式:
@FunctionalInterface interface Functional { String goodbye(String arg); } interface FunctionalNoAnn { String goodbye(String arg); } /* @FunctionalInterface interface NotFunctional { String goodbye(String arg); String hello(String arg); } 产生错误信息: NotFunctional is not a functional interface multiple non-overriding abstract methods found in interface NotFunctional */ public class FunctionalAnnotation { public static void main(String[] args) { Functional fl = a -> "Goodbye, " + a; FunctionalNoAnn fnal = a -> "Goodbye, " + a; } }
java.util.function
包旨在创建一组完整的目标接口,使得我们一般情况下不需再定义自己的接口。主要因为基本类型的存在,导致预定义的接口数量有少许增加。 一般顾名思义就能知道特定接口的作用。
方法引用
- 使用场景:当需要传递给
lambda
体的操作,已经有实现的相同结构的方法了,可以使用方法引用。lambda
表达式是对匿名内部类的简写,方法引用就是拿其它类的方法实例来实现自己接口中的抽象方法。- 被引用的方法所在的类不需要实现对应的函数式接口。
简单来说就是可以引用其它类中方法的具体实现来实例化我们需要的函数式接口。
方法结构要求
R:返回值
T1:参数1, T2:参数2
方法名:无需相同
抽象方法结构:R 方法名(T1)
引用方法结构:R 方法名(T1) 或 R T1.方法名()抽象方法结构:R 方法名(T1 T2)
引用方法结构:R 方法名(T1, T2) 或 R T1.方法名(T2)
@FunctionalInterface interface LambdaInter { void printStr(String s); } // 不需要实现接口 public class LambdaTest { @Test public void test() { LambdaTest lambdaTest = new LambdaTest(); LambdaInter lambdaInter = lambdaTest::LambdaTestPrintStr; lambdaInter.printStr("LambdaInter"); } // void printStr(String s); 与抽象方法结构相同:viod 方法名(形参相同) public void LambdaTestPrintStr(String s) { System.out.println(s); } }
当引用方法是
static
或者结构为 T1.方法名([T2])时,方法引用是用类名引用。
static
为正常的静态调用。结构为 T1.方法名([T2])时(未绑定的方法引用),类名引用的意义是明确调用者的类型。
构造函数引用
构造函数引用和方法引用类似。
class Dog { String name; int age = -1; // For "unknown" Dog() { name = "stray"; } Dog(String nm) { name = nm; } Dog(String nm, int yrs) { name = nm; age = yrs; } } interface MakeNoArgs { Dog make(); } interface Make1Arg { Dog make(String nm); } interface Make2Args { Dog make(String nm, int age); } public class CtorReference { public static void main(String[] args) { MakeNoArgs mna = Dog::new; // [1] Make1Arg m1a = Dog::new; // [2] Make2Args m2a = Dog::new; // [3] Dog dn = mna.make(); Dog d1 = m1a.make("Comet"); Dog d2 = m2a.make("Ralph", 4); } }
也可以使用 java.util.function
自带的函数接口。
public class CtorReference { public static void main(String[] args) { MakeNoArgs mna = Dog::new; // [1] Make1Arg m1a = Dog::new; // [2] Make2Args m2a = Dog::new; // [3] Dog dn = mna.make(); Dog d1 = m1a.make("Comet"); Dog d2 = m2a.make("Ralph", 4); Supplier<Dog> supplier = Dog::new; Function<String, Dog> function = Dog::new; BiFunction<String, Integer, Dog> function1 = Dog::new; Dog dog = supplier.get(); Dog dog1 = function.apply("fun"); Dog dog2 = function1.apply("fun", 10); } }