Java初学者或者匿名内部类忘记了的,在读这篇文章之前.推荐大家先读一下我的上一篇文章,java基础之匿名内部类.会对你学习lamdba表达式有所帮助;
链接在这里
↓↓↓↓↓↓↓↓↓↓
Lamdba 表达式的作用和语法
主要作用是代替匿名内部类的繁琐语法
语法 :
(形参列表) -> {
代码块
}
# 形参列表==函数式接口方法的形参列表
# 形参列表 key 与 value 的类型都可以省略掉,它会一句程序的上下文自动推断
# -> 英文字符 程序员常识.不多讲
# { 代码块 }
map.forEach((key,value)->{
System.out.println(key+ " : " + value);
});
# 详情请大家看文章最后的代码.如果不太懂自己copy出来跑一下就感觉清晰了
三部分组成
- (形参列表)-->形参列表允许省略形参类型.如果形参列表中只有一个参数.甚至连形参列表汇总的圆括号也可以省略
- 箭头(->). 必须由英文中的画线号和大于号组成 [程序员常识.我想没人会用中文的吧!!!!!]
- 代码块.
- 如果代码块只包含一条语句,Lamdba表达式允许省略代码块的花括号,那么这条语句就不要用花括号标识语句结束.
- lamdba代码块只有一条return语句.甚至可以省略return关键字.
- Lamdba表达式需要返回值,而它的代码块中仅有一条省略了return的语句.lamdba表达式会自动返回这条语句的值.
# 方法名与类名 接口名 没有实际意义.处女座请不要纠结---
# 创建一个具有返回值的函数式接口 方便演示return的情况
@FunctionalInterface
interface Ainter {
Integer getMax(Integer a, Integer b);
}
@Test
public void test(){
System.out.println("----------Lamdba 只有一条return 语句的情况下省略return与花括号--------");
// 接口方法的参数与 lamdba表达式参数接口一致
Ainter ainter = (a, b) -> a - b > 0 ? a : b;
Integer max = ainter.getMax(3, 21);
System.out.println(max);
}
三个限制
- Lambda表达式的形参列表与函数式接口中唯一的抽象方法的形参列表相同
- 看上面示例 ,接口方法的参数与 lamdba表达式接口一致
- Lamdba表达式的目标类型必须是明确的函数式接口
- Lamdba表达式只能为函数式接口创建对象.Lamdba表达式只能实现一个方法,因此它只能为函数式接口创建对象
保证Lamdba表达式的目标类型是一个明确的函数式接口
- 将Lamdba表达式赋值给函数式接口类型的变量
- 将Lamdba表达式作为函数式接口类型的参数传给某个方法
- 使用函数式接口对Lamdba表达式进行强制转换
- 参考下面的示例加强理解
//JVM编译的时候提醒检测是否是函数式接口
@FunctionalInterface
interface fire {
//定义一个类方法
static String getMethodName() throws NoSuchMethodException {
return fire.class.getMethod("getMethodName").getName();
}
//定义一个抽象方法
void ff(String str);
// 默认方法
default int getNum(int a, int b) {
return a + b;
}
}
public class LamdbaDemo {
// 方法的参数是接口
public void lamdbaTest(fire fire) {
System.out.println("=======lamdba表达式==========");
}
@Test
public void test1() {
LamdbaDemo lamdbaDemo = new LamdbaDemo();
//将Lamdba表达式赋值给函数式接口类型的变量
fire f2 = str -> System.out.println("lamdba 为函数式接口而生,它的目标就是函数式接口");
//将Lamdba表达式作为函数式接口类型的参数传给某个方法
lamdbaDemo.lamdbaTest(str -> System.out.println("lamdba 为函数式接口而生,它的目标就是函数式接口"));
//使用函数式接口对Lamdba表达式进行强制转换
Object f3 =(fire) str -> System.out.println("lamdba 为函数式接口而生,它的目标就是函数式接口");
}
}
完整代码请参见文章最下方
Lamdba 表达式与函数式接口[请参考上面的示例理解]
- Lamdba 表达式的类型.也被称为 "目标类型 "
- Lamdba表达式的目标类型必须是函数式接口
- 函数式接口: 只有一个抽象方法的接口.
- 函数式接口可以声明多个默认方法, 类方法, 但只能声明一个抽象方法.
java8在java.util.function包下.预定义了很多函数式接口,典型地包含如下四类接口.
- XxxFunction: 这类接口中通常包含一个apply()抽象方法.该方法对参数进行处理.转换(apply()方法的处理逻辑由LAMD吧表达式类实现),然后返回一个新的值。该函数式接口通常用于对指定数据进行转换处理。
- XxxConsumer:这类接口中通常包含一个accept()抽象方法.该方法与XxxFunction接口中的apply()方法基本相似,也负责对参数进行处理,只是该方法不会反悔处理结果。
- XxxxPredicate:这类接口中通常包含一个test()抽象方法,该方法通常用来对参数进行某种判断(test()方法的判断逻辑由Lamdba表达式来实现),然后返回一个boolean值。该接口通常用来判断参数是否满足特定条件。经常用于进行筛选数据。
- XxxSupplier:这类接口通常包含一个getAsXxx() 抽象方法,该方法不需要输入参数,该方法会按某种逻辑算法(getAsXxx()方法的逻辑算法由Lamdba表达式来实现)返回一个数据.
eg:
方法引用于构造器引用
如果代码块只包含一条语句,Lamdba表达式允许省略代码块的花括号,那么这条语句就不要用花括号标识语句结束,不仅如此,如果Lamdba表达式的代码块只有一条代码.还可以在代码块中使用方法引用和构造器引用;
语法:
两个英文冒号 ::
引用类方法:
引用类方法 重点在于类方法-->可以直接由类调用的方法; 被 public static 修饰的方法
引用特定对象的实例
引用特定对象的实例方法. 重点还是在于特定对象 "string" 对象 实例方法: indexof,subString....
引用某类对象的实例方法
引用某类对象的实例方法与引用某类特定对象的实例.道理其实是一样的..
引用构造器
同上
System.out.println("-----------引用类方法");
list.forEach(System.out::println);
System.out.println("-----------某类对象的特殊引用方法");
Ainter aa = (a, b) -> a.compareTo(b);
System.out.println("=======引用特定对象实例的方法=======");
Conver converter = "fkit.org"::indexOf;
System.out.println("=======引用构造器=======");
Ttttt ttttt = Integer::new;
System.out.println(ttttt.get(1));
# 完整代码参加文章最下方
Lamdba与匿名内部类的区别
- Lamdba表达式和匿名内部类的区别如下:
- 匿名内部类可以为任意接口创建实例---不管接口包含多少个方法.但是Lamdba只能实现函数式接口;
- 匿名内部类可以为抽象类甚至普通类创建实例;但lamdba 表达式只鞥你为函数式接口创建实例
- 匿名内部类实现的抽象方法的方法体允许调用接口中定义的默认方法;但Lamdba表达式的代码块不允许调用接口中定义的默认方法.
总结一下就是函数式接口成就了lamdba也限制了lamdba;
下面贴上练习代码:
package com.zhaluobox.crazyjava.chapter06.section08;
import org.junit.Test;
import java.util.*;
import java.util.function.Consumer;
/**
* 定义一个函数式接口
*/
@FunctionalInterface
interface Ttttt {
Integer get(int title);
}
//JVM编译的时候提醒检测是否是函数式接口
@FunctionalInterface
interface fire {
//定义一个类方法
static String getMethodName() throws NoSuchMethodException {
return fire.class.getMethod("getMethodName").getName();
}
//定义一个抽象方法
void ff(String str);
// 默认方法
default int getNum(int a, int b) {
return a + b;
}
}
@FunctionalInterface
interface Conver {
Integer convert(String from);
}
@FunctionalInterface
interface Ainter {
Integer getMax(Integer a, Integer b);
}
/**
* 演示lamdba
*/
public class LamdbaDemo {
public void lamdbaTest(fire fire) {
// System.out.println("=======lamdba表达式==========");
}
@Test
public void test1() {
LamdbaDemo lamdbaDemo = new LamdbaDemo();
//将Lamdba表达式赋值给函数式接口类型的变量
fire f2 = str -> System.out.println("lamdba 为函数式接口而生,它的目标就是函数式接口");
//将Lamdba表达式作为函数式接口类型的参数传给某个方法
lamdbaDemo.lamdbaTest(str -> System.out.println("lamdba 为函数式接口而生,它的目标就是函数式接口"));
//使用函数式接口对Lamdba表达式进行强制转换
Object f3 =(fire) str -> System.out.println("lamdba 为函数式接口而生,它的目标就是函数式接口");
System.out.println("=======匿名内部类==========");
String s = "燃烧我的卡路里";
fire f = new fire() {
@Override
public void ff(String str) {
System.out.println(getNum(1, 2)); //可以调用默认方法
System.out.println(str);
}
};
f.ff(s);
System.out.println(f.getNum(1, 2));
System.out.println("=======lamdba表达式==========");
/**
* 相比较之下lamdba表达式只能去实现函数式 接口中的抽象方法.其他统统不能
* @param s1 形参列表与函数式接口中的唯一抽象方法的形参是一样的
* 代码块中不能调用默认方法
*/
fire fire = (s1) -> {
System.out.println(s1);
// System.out.println(getNum(1,2)); //Lamdba 代码块中不允许调用默认方法
};
fire.ff(s);
fire.getNum(1, 2);
try {
String methodName = com.zhaluobox.crazyjava.chapter06.section08.fire.getMethodName();
System.out.println(methodName);
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
System.out.println("----------Lamdba 只有一条return 语句的情况下省略return与花括号--------");
Ainter ainter = (a, b) -> a - b > 0 ? a : b;
Integer max = ainter.getMax(3, 21);
System.out.println(max);
}
/**
* 下面以 java8 改进的forEach() 方法为例演示lamdba 表达式
*/
@Test
public void arrTest() {
List<Integer> list = new ArrayList<>();
list.add(12);
list.add(13);
list.add(1312);
list.add(1311);
list.add(12);
System.out.println("---------------增强for遍历");
for (Integer ii : list) {
System.out.println(ii + 3);
}
System.out.println("----------------传统lamdba的方式");
list.forEach((i) -> {
System.out.println(i + 3);
});
System.out.println("------------------------------------------------------------");
list.forEach(i -> System.out.println(i + "省略形参圆括号() 与 代码块只有一条一句省略花括号"));
System.out.println("=========传入匿名内部类的方式======");
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.println(integer);
}
});
System.out.println("-----------引用类方法");
list.forEach(System.out::println);
System.out.println("-----------某类对象的特殊引用方法");
Ainter aa = (a, b) -> a.compareTo(b);
System.out.println("=======引用特定对象实例的方法=======");
Conver converter = "fkit.org"::indexOf;
System.out.println("=======引用构造器=======");
Ttttt ttttt = Integer::new;
System.out.println(ttttt.get(1));
}
@Test
public void mapTest() {
HashMap<String, Object> map = new HashMap<>();
map.put("西游记", 112);
map.put("水浒传", 993);
map.put("红楼梦", 322);
map.put("三国演义", 998);
System.out.println("--------------------map的原始遍历方式 1-----------------------");
Set<String> keySet = map.keySet();
for (String key : keySet) {
System.out.println(key + " : " + map.get(key));
}
System.out.println("--------------------map的原始遍历方式 2-----------------------");
Set<Map.Entry<String, Object>> entries = map.entrySet();
for (Map.Entry<String, Object> mp : entries) {
String key = mp.getKey();
Object value = mp.getValue();
System.out.println(key + " : " + value);
}
System.out.println("--------------------map的Lamdba遍历方式 3-----------------------");
map.forEach((key, value) -> {
System.out.println(key + " : " + value);
});
}
}
有不同观点.或者文章描述有错误.欢迎大家留言讨论