目录
先来看用普通的方法来完成集合的筛选,不用匿名内部类和lambda表达式
用测试类的方式来测试代码
如何使用这种测试类来测试代码,看这篇博客
@Test
public void show1(){
List<Student> list1 = filterStudentAge(list);
for(Student s:list1){
System.out.println(s);
}
System.out.println("==================");
List<Student> list2 = filterStudentScore(list);
for(Student s : list2){
System.out.println(s);
}
}
public List<Student> filterStudentAge(List<Student> list){
//找出所有年龄超过30岁的学生
List<Student> list1 = new ArrayList<Student>();
for(Student s : list){
if(s.getAge()>30){
list1.add(s);
}
}
return list1;
}
public List<Student> filterStudentScore(List<Student> list){
//找出所有成绩大于50分的学生
List<Student> list1 = new ArrayList<Student>();
for(Student s : list){
if(s.getScore()>50){
list1.add(s);
}
}
return list1;
}
运行测试show方法后
完成了年龄的筛选,选出了年龄大于30的学生对象
完成了成绩的筛选,选出了成绩大于50的学生对象
方式二,用接口的方式,将判断集合是否满足某个条件,这个功能放入接口中。再用不同的具体实现类实现不同的筛选条件
接口
package lambdaDemo;
public interface filterInterface<Student> {
public boolean judge(Student s);
}
实现接口的类
package lambdaDemo;
public class filterStudentByAge implements filterInterface<Student> {
public boolean judge(Student s) {
return s.getAge() > 30;
}
}
package lambdaDemo;
public class filterStudentByScore implements filterInterface<Student>{
public boolean judge(Student s) {
return s.getScore()>50;
}
}
具体的方法和测试方法
//将实现类作为参数传进方法中,然后里面实现类中的方法完成筛选
public List<Student> filterStudent(List<Student> list, filterInterface<Student> f){
List<Student> list1 = new ArrayList<Student>();
for(Student s : list){
if(f.judge(s)){
list1.add(s);
}
}
return list1;
}
//测试方法
@Test
public void show2(){
List<Student> list1 = filterStudent(list,new filterStudentByAge());
for(Student s : list1){
System.out.println(s);
}
System.out.println("==================");
List<Student> list2 = filterStudent(list,new filterStudentByScore());
for(Student s : list2){
System.out.println(s);
}
}
这样写的好处是只要一个方法,实现不同的功能可以靠不同的实现类
但是这样写要实现的类太多
这里用到了策略设计模式
方法括号里面写接口引用变量,实际传入 实现类 的对象
给方法什么策略,就按什么过滤,策略是按年龄筛选,实际方法中就按年龄筛选
方式三、匿名内部类
//具体实现筛选的类,接口的实现类将作为参数传入这个方法,引用变量f调用实现类的方法
public List<Student> filterStudent2(List<Student> list,filterInterface<Student> f){
List<Student> list1 = new ArrayList<Student>();
for(Student s : list){
if(f.judge(s)){
list1.add(s);
}
}
return list1;
}
//不需要新建实现类来实现接口功能
//在传入参数的时候,用匿名内部类的方式写实现类
@Test
public void show3(){
List<Student> list1 = filterStudent2(list, new filterInterface<Student>() {
public boolean judge(Student s){
return s.getAge()>40;
}
});
for(Student s : list1){
System.out.println(s);
}
System.out.println("=======================");
List<Student> list2 = filterStudent2(list, new filterInterface<Student>() {
public boolean judge(Student s) {
return s.getScore()>50;
}
});
for(Student s : list2){
System.out.println(s);
}
}
方式二接口中的filterStudent方法与方式三匿名内部类的filterStudent2方法内容一样
只不过为了方便看,又写了这个filterStudent2方法
下面的方式四、lambda表达式,就不再重复写这个方法了,直接用方式二中的filterStudent方法
方式四、lambda
@Test
public void show4(){
List<Student> list1 = filterStudent(list , (e) -> e.getAge()>30);
list1.forEach(System.out::println);
}
什么都不需要,接口和具体类都不用,只要这两行代码,就可以完成之前的所有任务
lambda语法的基本格式
Java8中引入了一个新的操作符“->",该操作符称为箭头操作符或者Lambda操作符
箭头操作符将Lambda表达式拆分成两部分
左侧:Lambda 表达式的参数列表
右侧:Lambda 表达式中所需执行的功能,即lambda体
lambda表达式 (e) -> e.getAge()>30 实际上是对之前的接口的具体实现
public interface filterInterface<Student> {
public boolean judge(Student s);
}
我们原先使用匿名内部类实现这个接口,现在可以用lambda表达式的方式来实现
箭头操作符的左侧,也就是(e)对应的是接口的抽象方法中的参数列表(Student s),有几个参数,lambda括号中的参数就有几个
箭头操作符的右侧,e.getAge()>30 写的是接口抽象方法的具体实现
注意lambda只适用于函数式接口,接口里面有且仅有一个抽象方法
可以使用注解@FunctionalInterface修饰,这个修饰的作用是,检查是否是函数式接口
@FunctionalInterface
public interface test1 {
public void show();
}
此时如果多写一个抽象方法,就会提示错误
lambda表达式的语法格式
语法格式一:无参数,无返回值
左侧无参就直接写小括号
这是接口
package lambdaDemo1;
public interface test1 {
public void show();
}
分别用匿名内部类与lambda表达式来实现接口
package lambdaDemo1;
import org.junit.Test;
public class demo1 {
@Test
public void method1(){
//用匿名内部类来实现接口
test1 t = new test1() {
@Override
public void show() {
System.out.println("匿名内部类的show");
}
};
t.show();
System.out.println("================");
//用lambda表达式来实现接口
test1 t1 = () -> System.out.println("lambda表达式的show");
t1.show();
}
}
结果为
这里对象 t 和 t1 都能调用接口里面的抽象方法show,是因为匿名内部类和lambda实质上都是对这个接口的具体实现。
lambda是将一个实现接口的具体类浓缩为了一句表达式,这句表达式就是接口的show方法的重写实现
() -> System.out.println(“lambda表达式的show”);
public void show();
箭头操作符的左侧(), 对应show()的参数列表()
箭头操作符的右侧System.out.println(“lambda表达式的show”);
相当于这个方法的方法体
public void show(){
System.out.println("lambda表达式的show");
}
注意
package lambdaDemo1;
import org.junit.Test;
public class demo1 {
@Test
public void method1(){
int num = 10;
//匿名内部类可以调用同级别的局部变量
//lambda也可以调用,不过和匿名内部类一样,这个局部变量是final修饰的,无法改变
test1 t2 = () -> System.out.println("lambda"+num);
t2.show();
}
}
语法格式二,有一个参数,并且无返回值
之前Student类的例子就是这个,这里就不再举例了
语法格式三,有两个参数,有返回值,并且lambda体中有多条语句
如果要使用多条语句,那么lambda体必须要有大括号
package lambdaDemo1;
public interface test2 {
public boolean compare(int a,int b);
}
package lambdaDemo1;
import org.junit.Test;
public class demo2 {
@Test
public void method(){
test2 t = (x,y) -> {
System.out.println("x与y比较大小");
return x>y ? true : false;
};
boolean b = t.compare(3,2);
System.out.println(b);
}
}
语法格式四,有两个参数,有返回值,并且lambda体中只有一条语句
此时,lambda表达式中的大括号和return关键词都可以省略
上面的那个数字比较的lambda表达式例子就可以简写为
@Test
public void method2(){
test2 t = (x,y) -> x>y ? true : false;
System.out.println(t.compare(4,2));
}
语法格式五,lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出,数据类型,即“类型推断”
例子就用上面的(x,y)即可,这里的x与y都没有定义数据类型
实例运用
需求:对一个数进行运算
步骤:
- 写一个接口,接口的抽象方法接收一个数字,并且返回一个数字
public interface test3 {
public Integer getValue(Integer i);
}
- 写一个方法,这个方法要对数字进行运算
如果直接写public Integer operation(Integer num),这样写就等于锁死了数字的处理方式,每进行一种操作,就需要写一个方法。比如方法体是这个num再加上一个数,如果想换成减去一个数,就又要写一个新的方法,因此要加进去刚才写的接口
public Integer operation(Integer num, test3 t){
return t.getValue(num);
}
- 这里虽然用上了接口,但是具体做什么运算没有指定,现在就需要再写一个新的方法,方法中调用operation方法,并且用lambda表达式实现接口的具体功能
@Test
public void method(){
//完成平方操作
Integer i = operation(10,(x) -> x*x);
System.out.println(i);
}
这里也可以通过更改lambda表达式的方式,来修改具体实现的运算功能
@Test
public void method(){
//完成平方操作
Integer i = operation(10,(x) -> x*x);
System.out.println(i);
//将这个数加上100
System.out.println(operation(20,(y) -> y+100));
}