1.体验方法引用
方法引用的出现原因
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作
那么考虑一种情况:如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那是否还有必要再写重复逻辑呢?答案肯定是没有必要
那我们又是如何使用已经存在的方案的呢?
这就是我们要讲解的方法引用,我们是通过方法引用来使用已经存在的方案
package yingyong;import java.util.Arrays;
import java.util.Comparator;public class demo1 {public static void main(String[] args) {//需求:创建一个数组,进行倒序排列Integer[] arr = {3, 5, 4, 1, 6, 2};//匿名内部类Arrays.sort(arr, new Comparator<Integer>() {@Overridepublic int compare(Integer o1, Integer o2) {return o2 - o1;}});//lambda表达式//因为第二个参数的类型Comparator是一个函数式接口Arrays.sort(arr, (Integer o1, Integer o2)-> {return o2 - o1;});//lambda表达式简化格式Arrays.sort(arr, ( o1, o2)-> o2 - o1);//方法引用//1.引用处需要是函数式接口//2.被引用的方法需要已经存在//3.被引用方法的形参和返回值需要跟抽象方法的形参和返回值保持一致//4.被引用方法的功能需要满足当前的要求Arrays.sort(arr,demo1::mySort);//表示引用demo1类里面的mySort方法//把这个方法当做抽象方法的方法体}public static int mySort(int o1, int o2) {return o2 - o1;}
}
2.方法引用符
方法引用符
:: 该符号为引用运算符,而它所在的表达式被称为方法引用
推导与省略
如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
如果使用方法引用,也是同样可以根据上下文进行推导
方法引用是Lambda的孪生兄弟
3.引用类方法
引用类方法,其实就是引用类的静态方法
格式
类名::静态方法
范例
Integer::parseInt
Integer类的方法:public static int parseInt(String s) 将此String转换为int类型数据
练习描述
定义一个接口(Converter),里面定义一个抽象方法 int convert(String s);
定义一个测试类(ConverterDemo),在测试类中提供两个方法
一个方法是:useConverter(Converter c)
一个方法是主方法,在主方法中调用useConverter方法
package yingyong;import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Function;public class demo2 {public static void main(String[] args) {/*需求:集合里一下的数字,把他们变成Int类型"1","2","3","4","5"*///1.常规方法ArrayList<String> list1 = new ArrayList<>();Collections.addAll(list1, "1", "2", "3", "4", "5");ArrayList<Integer> list2 = new ArrayList<>();for (String s : list1) {int i = Integer.parseInt(s);list2.add(i);}System.out.println(list2);System.out.println("---------------");//2.stream流list1.stream().map(new Function<String, Integer>() {@Overridepublic Integer apply(String s) {int i = Integer.parseInt(s);return i;}}).forEach(s-> System.out.println(s));System.out.println("---------------");//简化lambda表达式list1.stream().map( s-> Integer.parseInt(s)).forEach(s-> System.out.println(s));System.out.println("---------------");//3.引用类方法list1.stream().map(Integer::parseInt).forEach(s-> System.out.println(s));}
}
-
使用说明
Lambda表达式被类方法替代的时候,它的形式参数全部传递给静态方法作为参数
4.引用对象的实例方法
引用其他类的成员方法
package yingyong;import java.util.ArrayList;
import java.util.Collections;
import java.util.function.Predicate;/*方法引用(引用成员方法)格式其他类:其他类对象::方法名本类:this::方法名(引用处不能是静态方法)父类:super::方法名(引用处不能是静态方法)需求:集合中有一些名字,按照要求过滤数据数据:"张无忌","周芷若","赵敏","张强","张三丰"要求:只要以张开头,而且名字是3个字的*/
public class demo3 {public static void main(String[] args) {//1.创建集合ArrayList<String> list = new ArrayList<>();//2.添加数据Collections.addAll(list, "张无忌", "周芷若", "赵敏", "张强", "张三丰");//1.stream()流方法// 要求:只要以张开头,而且名字是3个字的list.stream().filter(s -> s.startsWith("张")).filter(s -> s.length() == 3).forEach(s -> System.out.println(s));//引用类方法 ---其他类list.stream().filter(new Predicate<String>() {@Overridepublic boolean test(String s) {return s.startsWith("张") && s.length() == 3;}}).forEach(s -> System.out.println(s));System.out.println("-----------------");//我们现在就在其他类自己写一个这个接口的实现类方法// 其他类:其他类对象::方法名list.stream().filter(new Mytest()::test).forEach(s -> System.out.println(s));}
}
package yingyong;public class Mytest {public boolean test(String s) {return s.startsWith("张") && s.length()==3;}
}
引用本类成员方法:this::方法名(引用处不能是静态方法)
public class demo4 {public static void main(String[] args) {new test();}}
package yingyong;import java.util.ArrayList;
import java.util.function.Predicate;public class test {public test(){method01();}private void method01() {//创建集合 初始化集合数据ArrayList<Integer> list = new ArrayList<>();for (int i = 0; i < 10; i++) {list.add(i);}//调用方法过滤奇数的数字method02(list);}private void method02(ArrayList<Integer> list) {list.stream().filter(this::fun).forEach(s->System.out.println(s));}public boolean fun(int i) {return i % 2 == 0;}
}
5.引用构造器
为他专门写一个构造方法
public class Student {private String name;private int age;public Student() {}public Student(String str) {String[] arr = str.split(",");this.name = arr[0];this.age = Integer.parseInt(arr[1]);}public Student(String name, int age) {this.name = name;this.age = age;}
}
package yingyong;import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Function;
import java.util.stream.Collectors;/*方法引用(引用构造方法)格式类名::new目的:创建这个类的对象需求:集合里面存储姓名和年龄,要求封装成Student对象并收集到List集合中方法引用的规则:1.需要有函数式接口2.被引用的方法必须已经存在3.被引用方法的形参和返回值,需要跟抽象方法的形参返回值保持一致4.被引用方法的功能需要满足当前的需求*/
public class demo5 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "张三,18", "李四,22", "王五,25");//1.匿名内部类方法list.stream().map(new Function<String, Student>() {@Overridepublic Student apply(String s) {String name = s.split(",")[0];int age = Integer.parseInt(s.split(",")[1]);return new Student(name, age);}}).forEach(s -> System.out.println(s));System.out.println("---------------");//2.lambda方法list.stream().map(s-> new Student(s.split(",")[0],Integer.parseInt(s.split(",")[1]))).forEach(s -> System.out.println(s));System.out.println("---------------");// 3.方法引用(引用构造方法)/* 格式类名::new*/List<Student> newlist = list.stream().map(Student::new).collect(Collectors.toList());System.out.println(newlist);}
}
6.类名引用成员方法
源码里的String里的把字符串变大写的方法
public String toUpperCase() {return toUpperCase(Locale.getDefault());}
为什么形参的参数没有对应上呢
下面来解释
抽象方法形参的详解:
第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法在Stream流当中,第一个参数一般都表示流里面的每一个数据。假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法局限性:不能引用所有类中的成员方法。是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。
package yingyong;import java.util.ArrayList;
import java.util.Collections;/*方法引用的规则:1.需要有函数式接口2.被引用的方法必须已经存在3.被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。4.被引用方法的功能需要满足当前的需求抽象方法形参的详解:第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法在Stream流当中,第一个参数一般都表示流里面的每一个数据。假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法局限性:不能引用所有类中的成员方法。是跟抽象方法的第一个参数有关,这个参数是什么类型的,那么就只能引用这个类中的方法。*/
public class demo6 {public static void main(String[] args) {/* 方法引用(类名引用成员方法)格式类名::成员方法需求:集合里面一些字符串,要求变成大写后进行输出*/ArrayList<String> list = new ArrayList<>();Collections.addAll(list, "abc", "bba", "ccd");//拿着流里面的每一个数据,去调用String类中的toUpperCase方法,方法的返回值就是转换之后的结果。list.stream().map(String::toUpperCase).forEach(s->System.out.println(s));}
}
7.引用数组的构造方法
package yingyong;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
/*
方法引用(数组的构造方法)
格式数据类型[]::new
目的:创建一个指定类型的数组
需求:集合中存储一些整数,收集到数组当中细节:数组的类型,需要跟流中数据的类型保持一致。*/
public class demo7 {public static void main(String[] args) {ArrayList<Integer> list = new ArrayList<>();Collections.addAll(list, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10);/*格式数据类型[]::new*//* 需求:集合中存储一些整数,收集到数组当中*/Integer[] arr = list.stream().toArray(Integer[]::new);System.out.println(Arrays.toString(arr));/* //收集到数组里Integer[] arr = list.stream().toArray(new IntFunction<Integer[]>() {@Overridepublic Integer[] apply(int value) {return new Integer[value];}});System.out.println(Arrays.toString(arr));*///}}
练习
ublic class Student {private String name;private int age;public Student() {}public Student(String name, int age) {this.name = name;this.age = age;}public Student(String str) {String[] arr = str.split(",");this.name = arr[0];this.age = Integer.parseInt(arr[1]);//张无忌,15}
}
package lx;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
/*需求:集合中存储一些字符串的数据,比如:张三,23。收集到Student类型的数组当中*/
public class demo1 {public static void main(String[] args) {ArrayList<String> list = new ArrayList<>();Collections.addAll(list,"张三,23","李四,24");//由于集合是String类型的所以要强制转换Student[] arr = list.stream().map(Student::new).toArray(Student[]::new);System.out.println(Arrays.toString(arr));}
}
练习2方法一
package lx;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.function.Function;
import java.util.function.IntFunction;/** 技巧:* 1.现在有没有一个方法符合我当前的需求* 2.如果有这样的方法,这个方法是否满足引用的规则* 静态 类名::方法名* 成员方法* 构造方法 类名::new* */
public class demo2 {public static void main(String[] args) {/* 需求:* 创建集合添加学生对象学生对象属性:name,age要求:* 获取姓名并放到数组当中使用方法引用完成*/Student s1 = new Student("张三", 18);Student s2 = new Student("李四", 19);Student s3 = new Student("王五", 28);ArrayList<Student> list = new ArrayList<>();Collections.addAll(list, s1, s2, s3);//先获取每一个流 ,把每一个流转换成String 类型的nameString[] arr = list.stream().map(new Function<Student, String>() {@Overridepublic String apply(Student student) {return student.getName();}}).toArray(new IntFunction<String[]>() {@Overridepublic String[] apply(int value) {return new String[value];}});System.out.println(Arrays.toString(arr));}
}
方法2
package lx;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;/** 技巧:* 1.现在有没有一个方法符合我当前的需求* 2.如果有这样的方法,这个方法是否满足引用的规则* 静态 类名::方法名* 成员方法* 构造方法 类名::new* */
public class demo3 {public static void main(String[] args) {/* 需求:* 创建集合添加学生对象学生对象属性:name,age要求:* 获取姓名并放到数组当中使用方法引用完成*/Student s1 = new Student("张三", 18);Student s2 = new Student("李四", 19);Student s3 = new Student("王五", 28);ArrayList<Student> list = new ArrayList<>();Collections.addAll(list, s1, s2, s3);String[] arr = list.stream().map(Student::getName).toArray(String[]::new);System.out.println(Arrays.toString(arr));}
}
抽象方法形参的详解:
第一个参数:表示被引用方法的调用者,决定了可以引用哪些类中的方法在Stream流当中,第一个参数一般都表示流里面的每一个数据。假设流里面的数据是字符串,那么使用这种方式进行方法引用,只能引用String这个类中的方法第二个参数到最后一个参数:跟被引用方法的形参保持一致,如果没有第二个参数,说明被引用的方法需要是无参的成员方法
对象名引用方法名只能---被引用方法的形参,需要跟抽象方法的第二个形参到最后一个形参保持一致,返回值需要保持一致。
类名引用方法名,由于引用的方法没有第二个参树,说明被引用的方法是需要无参的成员方法刚好满足
题目3
package lx;import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;/** 需求:* 创建集合添加学生对象* 学生对象属性:name,age* 要求:* 把姓名和年龄拼接成:张三-23的字符串,并放到数组当中* 使用方法引用完成* */
public class demo4 {public static void main(String[] args) {Student s1 = new Student("张三", 18);Student s2 = new Student("李四", 19);Student s3 = new Student("王五", 28);ArrayList<Student> list = new ArrayList<>();Collections.addAll(list, s1, s2, s3);String[] arr = list.stream().map(Student::pj).toArray(String[]::new);System.out.println(Arrays.toString(arr));}
}
public String pj(){return name+"-"+age;}