【练习14-1(LambdaArgumentDemo.java)】作为实参传递lambda表达式
lambda表达式可用在任何提供了目标类型的上下文中。前面示例中使用的上下文是赋值和初始化。另一种情况就是作为实参传递lambda表达式。事实上,这是lambda表达式的一种常见且强大的用途,因为可将可执行代码作为实参传递给方法,这极大地增强了Java的表达力。
为了演示过程,本练习中创建了三个字符串函数,它们执行以下操作:颠倒字符串、颠倒字符串中字母的大小写、用连字符替代空格。这些函数作为函数式接口StringFunc的lambda表达式实现。之和它们作为第一个实参传递给changeStr()方法。changeStr()方法将字符串函数应用于第二个实参传递给changeStr()的字符串并返回结果。因此,changeStr()方法可用于各种不同的字符串函数。
java
package javaone.a.beginners.guide.chapterfourteen;
// Use a lambda expression as an argument to a method.
interface StringFunc{
String func(String str);
}
public class ChapterFourteenProgramOne {
// This method has a functional interface as the type of its
// first parameter. Thus, it can be passed a reference to any
// instance of that interface, including an instance created
// by a lambda expression. The second parameter specifies the
// string to operate on.
static String changeStr(StringFunc sf, String s){
return sf.func(s);
}
public static void main(String[] args) {
String inStr = "Lambda Expressions Expand Java";
String outStr;
System.out.println("Here is input string: " + inStr);
// Define a lambda expression that reverses the contents
// of a string and assign it to a StringFunc reference varible.
StringFunc reverse = (str) -> {
String result = "";
for (int i = str.length() - 1; i >= 0 ; i--) {
result += str.charAt(i);
}
return result;
};
// Pass reverse to the first argument to changeStr().
// Pass the input string as the second argument.
outStr = changeStr(reverse, inStr);
System.out.println("The string reversed: " + outStr);
// This lambda expression replace spaces with hyphens.
// It is embedded directly in the call to changeStr().
outStr = changeStr((str)->str.replace(' ','-'), inStr);
System.out.println("The string with spaces replaced: " + outStr);
// This block lambda invert the case of the characters in the
// string. It is also embedded directly in the call to changeStr().
outStr = changeStr((str) -> {
String result = "";
char ch;
for (int i = 0; i < str.length(); i++) {
ch = str.charAt(i);
if(Character.isUpperCase(ch)){
result += Character.toLowerCase(ch);
}else{
result += Character.toUpperCase(ch);
}
}
return result;
}, inStr);
System.out.println("The string in reversed case: " + outStr);
}
}
14.9 自测题
- 什么是lambda运算符?
答案:lambda运算符是->。
- 什么是函数式接口?
答案: 函数式接口是指仅指定一个抽象方法的接口。
- 函数式接口和lambda表达式是如何关联的?
答案:lambda表达式提供函数式接口定义的抽象方法的实现。函数式接口定义目标类型。
- lambda表达式的两种常见类型是什么?
答案:lambda表达式的两种类型是表达式lambda和块lambda。表达式lambda指定单一的表达式,其值由lambda返回。而块lambda包含一个代码块,其值由return语句指定。
- 给出一个lambda表达式,如果某个数字在10和20之间(包括10和20),该表达式返回的结果为true。
答案:(n) -> (9 < n && n < 21);
- 创建一个函数式接口,使之支持习题5中所创建的lambda表达式。调用接口MyTest及其抽象方法testing()。
答案:
interface MyTest{
boolean testing(int n);
}
- 创建一个计算某个整数值的阶乘的块lambda。演示其用法,对于该函数式接口使用本章介绍的NumericFunc。
java
package javaone.a.beginners.guide.chapterfourteen;
interface NumericFuncOne{
int func(int n);
}
public class ChapterFourteenExerciseSeven {
public static void main(String[] args) {
// This block lambda computes the factorial of an int value.
NumericFuncOne factorial = (n) -> {
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
};
System.out.println("The factorial of 3 is " + factorial.func(3));
System.out.println("The factorial of 5 is " + factorial.func(5));
System.out.println("The factorial of 9 is " + factorial.func(9));
}
}
- 创建一个名为MyFunc<T>的泛型函数式接口。调用它的抽象方法func()。让func()返回一个T类型的引用,并让它接受T类型的形参(因此,MyFunc将是本章中所介绍的NumericFunc的泛型版本)。重写习题7的答案,演示其用法,这样就可以使用MyFunc<T>而不使用NumericFunc。
java
package javaone.a.beginners.guide.chapterfourteen;
interface MyFuncTwo<T>{
T func(T n);
}
public class ChapterFourteenExerciseEight {
public static void main(String[] args) {
// This block lambda computes the factorial of an int value.
MyFuncTwo<Integer> factorial = (n) -> {
int result = 1;
for (int i = 1; i <= n; i++) {
result *= i;
}
return result;
};
System.out.println("The factorial of 3 is " + factorial.func(3));
System.out.println("The factorial of 5 is " + factorial.func(5));
System.out.println("The factorial of 9 is " + factorial.func(9));
}
}
- 使用练习14-1所示的程序创建一个lambda表达式,删除字符串中的所有的空格并返回结果。通过将其传递给changeStr()演示该方法。
java
package javaone.a.beginners.guide.chapterfourteen;
interface StringFuncOne{
String func(String str);
}
public class ChapterFourteenExerciseNine {
static String changeStr(StringFuncOne sf, String s){
return sf.func(s);
}
public static void main(String[] args) {
String inStr = "Lambda Expressions Expand Java";
String outStr;
System.out.println("Here is input string: " + inStr);
outStr = changeStr((str)->str.replace(" ",""), inStr);
System.out.println("The string with spaces replaced: " + outStr);
}
}
- lambda表达式可使用局部变量吗?如果可以,必须满足什么约束条件?
答案:可以,但变量必须是有效的final。
- 如果lambda表达式抛出了经检查的异常,函数式接口中的抽象方法必须有一条包含该异常的throws子句。这种说法正确吗?
答案: 正确。
- 什么是方法引用?
答案:方法引用是指引用某个方法但不执行它的一种方法。
- 当被计算时,方法引用会创建一个由其上下文提供的_________的实例。
答案:函数式接口。
- 假定名为MyClass的类包含静态的抽象方法myStaticMethod(),请说明如何指定对该抽象方法的方法引用。
答案:myClass::myStaticMethod。
- 假定名为MyClass的类包含实例方法myInstMethod(),并假定MyClass的一个对象称为mcObj,请说明如何创建对mcObj对象的myInstMethod()方法的引用。
答案:mcObj::myInstMethod。
- 在MethodRefDemoTwo程序中,将新方法hasCommonFactor()添加到MyIntNum中。如果方法hasCommonFactor()的int实参和存储在调用对象MyIntNum中的值至少有一个公有因子,该方法就返回true。例如,9和12就有公有因子3,9和16就没有公有因子。请通过方法引用演示hasCommonFactor()的用法。
java
package javaone.a.beginners.guide.chapterfourteen;
interface IntPredicateTwo{
boolean test(int n);
}
class MyIntNumTwo{
private int v;
MyIntNumTwo(int x){
v = x;
}
int getNum(){
return v;
}
// Return true if n is a Factor of v.
boolean isFactor(int n){
return (v % n) == 0;
}
boolean hasCommonFactor(int n){
for (int i = 2; i < v/i; i++) {
if(((v % i) == 0) && ((n % i) == 0)){
return true;
}
}
return false;
}
}
public class ChapterFourteenExerciseSixteen {
public static void main(String[] args) {
boolean result;
MyIntNumTwo myIntNum = new MyIntNumTwo(12);
IntPredicateTwo ip = myIntNum::hasCommonFactor;
result = ip.test(9);
if(result){
System.out.println("Common factor found.");
}
}
}
- 如何指定构造函数引用?
答案:通过指定类名后紧随::,之后紧跟new可以创建构造函数引用。例如,MyClass::new。
- Java定义的几个预定义函数式接口都位于哪个包中?
答案:java.util.function。