java中lambda表达式的基本使用
在java中使用lambda表达式的话,需要注意两点:
- 一个是lambda表达式本身的语法
- 另一个是函数式接口
lambda语法
java8新增了一个操作符->,称为lambda操作符或箭头操作符,它将lambda表达式分为两部分,箭头之前的是参数,箭头之后的是动作。例如:
(int i, int j) -> i+j;
相当于:
public int add(int i, int j)
{
return i+j;
}
参数的类型也可以省略,java编译器会根据上下的语义推断出来:
(i, j) -> i+j;
如果只有一个参数,括号也可以省略:
i -> i+i;
若没有参数,则括号不能省略:
() -> System.out.println("helloworld!");
箭头的右边也可以是代码段,这个代码段可以写很长:
() -> {
System.out.println("helloworld!");
...
}
lambda和接口结合
在java中,lambda表达式一定要结合functional interface来使用,functional interface是指一个只包含一个抽象方法的接口。下面来看一个例子。这是一个functional interface:
public interface myInterface
{
public void run(int num);
}
然后,声明一个该类型的lambda表达式:
myInterface mif = num -> num * num;
int i = mif.run(6);
两个参数的例子也是一样:
public interface myInterface
{
public void run(int x , int y);
}
声明lambda表达式:
myInterface mif = (x, y) -> x + y;
int i = mif.run(5,6);
当然,使用代码块也能使lambda表达式更加复杂。
变量捕获
说到lambda表达式,有一个话题是绕不开的,就是闭包。但这个问题在java中被简化了好多。这个问题可以分为两种情况来讨论:
1. lambda表达式可以访问到所在的类中定义的变量(filed),也可以修改这个变量。
2. lambda表达式可以访问到外层代码块(enclosing scope)中定义的本地变量(local varable),但不能修改他们,并且,如果一个本地变量在lambda表达式中被读取的话,这个变量必须是final或按final的性质使用(final变量赋值以后就不能再任何地方再修改了)。
看下面的例子:
public class App {
private int filed= 10;
void method1() {
int varable = 10;
MyInterface myInterface = n -> {
filed += 2; //可读取,可修改
int m = varable; //可读取
//varable += 2; //不可修改
return 1;
};
//varable += 2; //已经在lambda表达式中被读取了,就是final了,不能被修改。
}
}
在本例中,varable是一个本地变量,在lambda表达式中只能被读取而不能被修改,并且,一旦这个变量在lambda表达式中被读取了,那么在任何地方就不能被修改了。
方法引用
lambda表达式的本质是一个匿名方法,但如果有一个方法的签名(参数列表和返回值)和functional interface的签名一样并且逻辑正好是你需要的,那么你可以使用方法引用的方式来将它赋值给你的functional interface,而无需再编写lambda表达式。方法引用是jdk1.8中被引入的新语法,它跟lambda表达式息息相关,从字面意思来看,方法引用指向一个方法,但不调用它。其实这个特性在很多编程语言中都已经支持了,java也终于支持了。像其它编程语言一样,加括号就是调用方法,不加括号就是引用方法。
方法引用可以引用四种类型的方法,分别是:静态方法,实例方法,泛型方法,构造方法,引种每种方法的语法有略有区别。下面分别看个例子。下面这个类包含三个方法,对应前三种类型:
public class MyClass {
public int instanceMethod(int number) {
return number * 2;
}
public static int staticMethod(int number) {
return number * 2;
}
public <t number="" extends=""> int genericMethod(T param) {
return param.intValue() * 2;
}
}
方法引用的操作符是两个冒号(::),这也是一个新的操作符。
引用静态方法
引用静态方法的语法是ClassName::methodName
myInterface = MyClass::staticMethod;
myInterface.run(5);
引用实例方法
应用实例方法的语法是instance::methodName
MyClass myClass = new MyClass();
myInterface = myClass::instanceMethod;
myInterface.run(5);
应用泛型方法
MyClass myClass = new MyClass();
myInterface = myClass::<integer>genericMethod;
myInterface.run(5);</integer>
引用构造方法
引用构造方法的语法是ClassName::new构造方法的返回值是它所在的类的类型。下面编写一个functional interface和一个构造函数。先来一个类和它的构造函数
public class Foo {
String msg1, msg2;
public Foo(String msg1, String msg2) {
this.msg1 = msg1;
this.msg2 = msg2;
}
}
在来一个functional interface
interface FooInterface {
Foo fooMethod(String m1, String m2);
}
这个里面的fooMethod的返回值是Foo,参数列表是两个String对象,与Foo类的构造函数的签名一致。下面是引用这个构造方法的方式
FooInterface fooInterface = Foo::new;
Foo fooObj = fooInterface.fooMethod("hello", "world");