1.介绍
java8提供了默认接口方法,Lambda表达式,方法引用和重复注解等新的特性及API
2.允许在接口中有默认方法实现
Java 8 允许我们使用default关键字,为接口声明添加非抽象的方法实现。这个特性又被称为扩展方法:
举例:
public interface PhoneService {
void call();
//接口中默认方法实现
default String who(String name) {
return "name"+name;
}
}
1
2
3
4
5
6
7
3.lambad表达式
java中lambad表达式的实现有两个结构。一个是lambad表达式自身。另一个是函数式接口。
lambad表达式本质上是一个匿名(未命名)方法。
函数式接口是仅包含一个抽象方法的接口。如Runnable接口
lambad表达式语法
(0个或多个参数) -> 语句块
如:() -> 3.14 ; (n) -> {return n*10;}
3.1 lambad 函数式接口
把一个lambad表达式赋给一个函数式接口的引用时。会自动创建实现函数式接口的一个类的实例。
举例:
定义一个函数式接口:
public interface PhoneService {
void call();
default String who(String name) {
return "name"+name;
}
}
1
2
3
4
5
6
7
测试:
public class LambadTest {
public static void main(String[] args) {
PhoneService service = () -> {System.out.println("call");};
service.call();
}
}
1
2
3
4
5
6
lambad表达式的参数要么显示指定。要么程序可以根据上下文做隐式的推断。
举例:
public interface DogService {
String who(String name);
}
public class LambadTest {
public static void main(String[] args) {
//单个表达式 可以省略{}
DogService service = (name) -> "name is:" + name;
//根据上下文推断上下文类型;多个表达式要指定{}
DogService service1 = (name) -> {
System.out.println("wangwang");
return "name is:" + name;
};
//强制指定参数类型
DogService service2 = (String name) -> {
return "name is:" + name;
};
//抽象方法只有一个参数时可以忽略()括号
DogService service3 = name -> {
return "name is" + name;
};
System.out.println(service.who("haha"));
System.out.println(service1.who("xiaozhang"));
System.out.println(service2.who("xiaoma"));
System.out.println(service3.who("xiaowang"));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
3.2 lambad 泛型函数式接口
lambad表达式自身不能指定类型参数。因此lambad表达式不能是泛型。但是与lambad是关联的函数式接口的参数可以是泛型。此时,lambad表达式中的参数有调用接口时指定的参数类型来决定具体的类型。
举例:
public interface SomeFunc<T> {
T fun(T t);
}
public class FanTest {
public static void main(String[] args) {
SomeFunc strFun = (str) -> str +"";
System.out.println(strFun.fun(1));
System.out.println("hello");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
3.3 作为参数传递lambad表达式
把lambad表达式作为参数传给函数体。这是lambad式最常见的用途。也是lambad最强大的表达式,因为可以将可执行代码块最为参数传递给方法。这极大的增强了java的表达能力。
为了将lambad表达式作为参数传递。接收lambad表达式的参数类型必须是与该lambad表达式兼容的函数式接口的类型。
举例:
public interface StringFunc {
String func(String n);
}
public class LambadAsArgumentDemo {
static String stringOp(StringFunc sf,String s) {
return sf.func(s);
}
public static void main(String[] args) {
String result = stringOp((n) -> n.toUpperCase(),"hello");
System.out.println(result);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
3.3 lambad表达式与异常
lambad表达式可以抛出异常。但是,如果抛出经检查异常,该异常必须与函数式接口的抽象方法的throws字句中列出的异常兼容。
举例:
public interface DoubleNumbicArrayFunc {
double func(double[] n) throws IllegalArgumentException;
}
public class LambadExceptionDemo {
public static void main(String[] args) {
double values[] = {1.0,2.1,3.2};
DoubleNumbicArrayFunc func = n -> {
if (n.length == 0) {
throw new IllegalArgumentException("array empty");
}
double sum = 0.0;
for (double c:n) {
sum = sum + c;
}
return sum / n.length;
};
System.out.println(func.func(values));
System.out.println(func.func(new double[0]));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
3.4 lambad表达式与变量捕获
lambad表达式可以获取或设置其外层类的实例或静态变量值。以及调用外层类定义的方法。
但是,当lambad使用其外层作用域定义的局部变量时,会产生一种特殊的情况,称为变量捕获。在这种情况下,lambad表达式只能使用实质上是final的局部变量。不能修改外层作用域的局部变量
举例:
public interface MyFunc {
int func(int n);
}
public class MyFuncDemo {
private int age = 24;
private static final String name = "zhangxj";
private void printName() {
System.out.println("my name is zxj");
}
public static void main(String[] args) {
int height = 175;
MyFuncDemo demo = new MyFuncDemo();
MyFunc func = n -> {
demo.printName();
System.out.println(demo.age);
demo.age = demo.age + 1;
System.out.println(demo.age);
System.out.println(MyFuncDemo.name);
System.out.println(height);
//对height只能访问。不能修改。否则报错
return n + height;
};
System.out.println(func.func(12));
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
lambad可以使用和修改其调用类的实例变量,只是不能使用外层作用域内的局部变量,除非该变量实质上是final 变量。
方法引用
方法引用提供了一种引用而不执行方法的方式。这种特性与lambad表达式关联,因为它也需要由兼容的函数式接口构成的目标上下文。
函数引用会创建函数式接口的一个实例。
3.5 静态方法的方法引用
ClassName::methodName
public interface StringFunc {
String func(String n);
}
public class MyStringOps {
public static String strUppcase(String str) {
return str.toUpperCase();
}
}
public class MethodRefDemo {
static String stringOp(StringFunc sf,String s) {
return sf.func(s);
}
public static void main(String[] args) {
//静态方法引用
String outStr = stringOp(MyStringOps::strUppcase,"lambads add power to java");
System.out.println(outStr);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
3.6 实例方法的引用
objRef::methodName
public interface StringFunc {
String func(String n);
}
public class MyStringOps {
public String strUppcase(String str) {
return str.toUpperCase();
}
}
public class MethodRefDemo {
static String stringOp(StringFunc sf,String s) {
return sf.func(s);
}
public static void main(String[] args) {
MyStringOps ops = new MyStringOps();
//引用方法实例
String outStr = stringOp(ops::strUppcase,"lambads add power to java");
System.out.println(outStr);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
3.7 泛型中的方法引用
在泛型类/泛型方法中。可以使用方法引用
public interface MyFunc<T> {
int func(T[] vals,T v);
}
public class MyArrayOps {
static <T> int countMatching(T[] vals,T v) {
int count = 0;
for (int i = 0; i < vals.length;i++) {
if (vals[i] == v) count++;
}
return count;
}
}
public class GenericMethodRefDemo {
static <T> int myOp(MyFunc<T> f,T[] vals,T v) {
return f.func(vals,v);
}
public static void main(String[] args) {
Integer[] vals = {1,2,3,4,4,4,5,6};
int result = myOp(MyArrayOps::countMatching,vals,4);
System.out.println(result);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
3.8 构造函数引用
className::new
可以把这个引用赋值给定义的方法与构造函数兼容的任何函数式接口的引用。
public interface MyFunc {
MyClass func(int n);
}
public class MyClass {
private int val;
public MyClass(int val) {
this.val = val;
}
public MyClass() {
this.val = 0;
}
public int getVal() {
return val;
}
}
public class ConstructRefDemo {
public static void main(String[] args) {
MyFunc myFunc = MyClass::new;
//创建MyClass的一个实例
MyClass myClass = myFunc.func(100);
System.out.println(myClass.getVal());
}
}