目录
一、Lambda表达式
传递方法/代码块(函数式程序语言设计),java8支持此项功能,通过java的Lambda表达式实现。
- 通常表示一个匿名函数
- Lambda表达可以当作参数,传递给其他高级函数.
Lambda表达式定义:
- 类似于匿名方法,一个没有名字的方法
- 参数,箭头,1/多个表达式语句组合,参数一般需要用()括号括住
- 可以忽略写参数的类型
- 坚决不声明返回值类型
- 没有public/protected/private/static/final等修饰符
- 单句表达式,将直接作为返回值,不能用大括号括住
- 多句,必须用大括号,带return语句,也算多句
特殊情况:
- 无参数的时候,仅包留括号“()”,箭头表达式
- 一个参数的时候,可以省略括号
- 如果有返回值,返回值类型会在上下午推断出来的,无需声明
- 如果只在某几个分治有返回值,这样会报错不合法。
Lambda表达式使用举例:
public class StringOrderTest {
public static void main(String[] args) {
String[] planets = new String[] {
"Mercury", "Venus", "Earth", "Mars",
"Jupiter", "Saturn", "Uranus",
"Neptune" };
System.out.println(Arrays.toString(planets));
System.out.println("=======================");
System.out.println("使用Lambda, 长度从小到大:");
Arrays.sort(planets,
(String first, String second)
-> first.length() - second.length());
System.out.println(Arrays.toString(planets));
System.out.println("使用Lambda, 长度从大到小:");
Arrays.sort(planets, (first, second) -> (-1) * (first.length() - second.length()));
System.out.println(Arrays.toString(planets));
System.out.println("使用Lambda, 长度从大到小:");
Arrays.sort(planets,
(String first, String second) ->
{
int result = (-1) * (first.length() - second.length());
return result;
}
);
System.out.println(Arrays.toString(planets));
}
}
输出:
[Mercury, Venus, Earth, Mars, Jupiter, Saturn, Uranus, Neptune]
=======================
=======================
使用Lambda, 长度从小到大:
[Mars, Venus, Earth, Saturn, Uranus, Mercury, Jupiter, Neptune]
使用Lambda, 长度从大到小:
[Mercury, Jupiter, Neptune, Saturn, Uranus, Venus, Earth, Mars]
使用Lambda, 长度从大到小:
[Mercury, Jupiter, Neptune, Saturn, Uranus, Venus, Earth, Mars]
二、函数式接口
Lambda表达式应用过程可以通过是实现函数式接口。
函数式接口:
- 是一个接口,符合java解耦定义
- 只包含一个未实现抽象方法的接口
- 可以包括其他的defult方法,static方法,private方法
- 由于只有一个未实现的方法,所以Lambda表达式可以自动填充上这个未实现的方法
- 采用Lambda表达式,可以自动创建一个(伪)嵌套类对象(没有及时嵌套类class文件产生),然后使用,比嵌套类更加轻量,更加简洁高效
比如上例中就相当于自动填充了Compartor接口中的compare方法。
值得注意的是,虽然Compartor接口中含有两个未实现的方法,为compare和equals方法,但是几乎任何实现Compartor接口的类,肯定继承了Object类,也就是有equals实现,所以与上述函数式接口定义不矛盾。
自定义函数式接口:
1.自定义一个接口函数,并且只含有一个未实现方法
补充:
- 可以写自带注解:@FunctionalInterface会检查接口内是否只有一个未实现的方法。
- jdk8引入了Lamdba表达式,接口中只有一个抽象方法时才会被填充,所以jdk8开始,接口中支持默认方法/静态方法/私有方法。
@FunctionalInterface
public interface StringChecker {
public boolean test(String s);
//public boolean test2(String s);
}
2.利用Lambda表达式将接口内的方法填充并使用
public class PredicateTest {
public static void main(String[] args) {
String[] planets = new String[] {
"Mercury", "Venus", "Earth", "Mars",
"Jupiter", "Saturn", "Uranus", "Neptune" };
StringChecker evenLength = s ->
{
if(s.length()%2 == 0)
return true;
return false;
};
for(String p : planets) {
if(evenLength.test(p)) {
System.out.println(p);
}
}
}
}
输出:
Mars
Saturn
Uranus
为解决大量重复性的函数式接口,会使源代码膨胀。所以系统自带函数式接口,位于java.util.function包中
常用的几个:
Preducate:test()方法:
示例:
import java.util.function.Predicate;
public class PredicateTest {
public static void main(String[] args) {
String[] planets = new String[] {
"Mercury", "Venus", "Earth", "Mars",
"Jupiter", "Saturn", "Uranus", "Neptune" };
Predicate<String> oddLength = s ->
s.length()%2 == 0 ? false:true;
for(String p : planets) {
if(oddLength.test(p)) {
System.out.println(p);
}
}
}
}
Consumer:accept()方法:
Function:apply()方法:(大写)
Supplier:get()方法:
三、方法引用
将方法作为变量,传递给其他方法。Lambda表达式支持传递现有的库函数,可分为以下几类,格式通常以::隔开:
1.类::静态方法,如Math.abs方法
示例:
public class ClassStaticMethodTest {
public static double worker(NumFunction nf, double num)//2.创建接口函数
{
return nf.calculate(num);
}
public static void main(String[] args) {
double a = -5.3;
double b = worker(Math::abs, a);//3.引用方法
System.out.println(b);
double c = worker(Math::floor, a);//引用方法
System.out.println(c);
}
}
interface NumFunction {//1.创建接口
double calculate(double num);
}
输出:
5.3
-6.0
2.类::实例方法
3.对象::实例方法
支持this::实例方法和super::实例方法。
如:
super::实例方法引用则是父类有此实例方法。
4.Class::new,调用某类构造函数,创造对象。
Supplier为get方法,返回数据工厂,没有参数。
Class[]::new,支持数组对象的引用。
回顾一下:IntFunction是接受一个参数返回一个值。
四、Lambda表达式应用
1.变量遮蔽
import java.util.function.Consumer;
public class LambdaScopeTest {
public int x = 0;//3
class FirstLevel {
public int x = 1;//2
void methodInFirstLevel(int x) {//1,来自5处
// The following statement causes the compiler to generate
// the error "local variables referenced from a lambda expression
// must be final or effectively final" in statement A:
//
// x = 99;
Consumer<Integer> myConsumer = (y) -> //6来自4处
{
System.out.println("x = " + x); // Statement A,来自1处
System.out.println("y = " + y);//7来自6处
System.out.println("this.x = " + this.x);//来自2处
System.out.println("LambdaScopeTest.this.x = " +//来自3处
LambdaScopeTest.this.x);
};
myConsumer.accept(x);//4,来自1处
}
}
public static void main(String... args) {
LambdaScopeTest st = new LambdaScopeTest();
LambdaScopeTest.FirstLevel fl = st.new FirstLevel();
fl.methodInFirstLevel(23);//5
}
}
输出:
x = 23
y = 23
this.x = 1
LambdaScopeTest.this.x = 0
2.Lambda的this指代
this只得是创建这个表达式得方法得this参数
public class ThisScopeTest {
private String name = "def";
public static void main(String[] args) {
new ThisScopeTest().test();
}
public void test() {
StringOperation obj = ()->
{
System.out.println(this.name);
System.out.println(getName());
};
obj.operate();
}
public String getName() {
return this.name;
}
}
interface StringOperation {
String name = "abc";
public void operate();
}
输出:
def
def
3.比较
lambda表达式与嵌套类比较
- 优先级比嵌套类高
- 短小精悍,本身可以自描述
- 无法创建命名实例,因为变量遮蔽,无法获取自身得引用(this)
方法引用与自定义Lambda表达式比较
- 方法引用优先级更高
- 系统自带得方法引用更加简洁高效
- 对于复杂得Lambda表达式,采用方法引用更加清晰,且更加容易维护
尽可能地使用标准函数式接口,就时java自带地常用地四个接口,让代码更简洁,更容易学习
使用Lambda表达式地使用场景:刚好只要需要实现接口中一个抽象方法的实例,这时候用Lambda表达式就行了。
参考中国大学mooc《java核心技术》