1、方法引用
/**
* 1.使用场景: 当要传递给lambda体的操作,已经有实现的方法了,可以使用方法引用
*
* 2.方法引用本质上就是lambda表达式,而lambda表达式本质上是一个函数式接口的实例,所以方法引用本质上也是函数式接口的实例
*
* 3.使用格式: 类/对象(方法的调用者) :: 方法名
*
* 4.具体分为如下三种情况!
* 情况一 对象 :: 非静态方法
* 情况二 类 :: 静态方法
* 情况三 类 :: 非静态方法(这个比较特殊!!)
*
* 5.方法引用的要求: 要求接口中抽象方法的形参列表和返回值类型,与方法引用的方法的形参列表和返回值类型相同!
* 例如:Consumer接口中的void accept(T t);
* 如果你想在这里使用方法引用,则调用的该方法,必须和接口中抽象方法的参数列表和返回值类型一致!
* 所以这里: void setxxx(T t) 是ok的, 而String getxxx()是不行的!!!
* 注:这个要求,只适用用情况一,二!
*/
public class MethodRefTest {
/**
* 情况一: 对象 :: 实例方法
* Consumer中的void accpet(T t);
* PrintStream中的void println(T t);
*/
@Test
public void test1(){
//一: 使用lambda表达式
Consumer<String> consumer = str -> System.out.println(str);
consumer.accept("大帅比");
//二:使用方法引用!
PrintStream ps = System.out;
Consumer<String> consumer2 = ps::println;
consumer2.accept("dashuaibi");
}
/**
* Supplier中的 T get()
* 相当于Employee中的String getName();
*/
@Test
public void test1_1(){
//一:使用lambda表达式
Employee employee = new Employee(1001,"wzj",22,50000);
Supplier<String> supplier = () -> employee.getName();
System.out.println(supplier.get());
//二:使用方法引用!
Supplier<String> supplier2 = employee::getName;
System.out.println(supplier2.get());
}
/**
* 情况一: 类 :: 静态方法
* Comparator接口中的int compare(T o1, T o2);
* Integer中的int compare(T o1, T o2)
*/
@Test
public void test2(){
//方式一: lambda表达式
Comparator<Integer> c1 = (o1, o2) -> Integer.compare(o1,o2);
System.out.println(c1.compare(1,2));
//方式二: 方法引用
Comparator<Integer> c2 = Integer::compareTo;
System.out.println(c2.compare(1,2));
}
/**
* Function接口是1.8出的!
* Function中的R apply(T t);
* Math中的 long round(Double d);
*注:就是传进去是这个类型,返回出来去另一个类型!
*/
@Test
public void test2_2(){
//方式一:原生写法
Function<Double,Long> function = new Function<Double, Long>() {
@Override
public Long apply(Double d) {
return Math.round(d);
}
};
System.out.println(function.apply(12.0));
//方式二: lambda表达式
Function<Double,Long> function2 = d -> Math.round(d);
System.out.println(function.apply(12.0));
//方式三: 方法引用
Function<Double,Long> function3 = Math::round;
System.out.println(function.apply(12.0));
}
/**
* 情况三 类 :: 实例方法 (有难度)
* Comparator接口中的int compare(T t1, T t2)
* String中的 int t1.compareTo(t2)
*
* 注:虽然上面参数不匹配,但是上面是拿第一个参数的类型作为调用者!
*/
@Test
public void test3(){
//方式一: lambda表达式
Comparator<String> c1 = (t1,t2) -> t1.compareTo(t2);
System.out.println(c1.compare("abc","abc"));
//方法引用: 这里就相当于,将T1的类型,作为调用者来调用compareTo(t2)方法
Comparator<String> c2 = String :: compareTo;
System.out.println(c2.compare("abc","abc"));
}
/**
* BiPredicate(1.8出现的)
* BiPredicate中的 boolean test(T t1, T t2)
* String中的 boolean t1.equals(t2)
*/
@Test
public void test3_3(){
//方式一: lambda表达式
BiPredicate<String,String> b = (t1, t2) -> t1.equals(t2);
System.out.println(b.test("abc","abc"));
//方法引用: 这里就相当于,将T1的类型,作为调用者来调用compareTo(t2)方法
BiPredicate<String,String> b2 = String::equals;
System.out.println(b2.test("abc","abc"));
}
/**
* Function接口(1.8出现的)
* Function中的 R apply(T t);
* Employee中的String getName()
*/
@Test
public void test3_4(){
Employee employee = new Employee(007,"wzj",22,100000);
//方式一: lambda表达式
Function<Employee,String> f = e -> e.getName();
System.out.println(f.apply(employee));
//方法引用: 这里就相当于,将T1的类型,作为调用者来调用compareTo(t2)方法
Function<Employee,String> f2 = Employee::getName;
System.out.println(f2.apply(employee));
}
}
演示
类 :: 非静态方法
(这个是最特别的)
/**
* 方法引用: 对象::实例方法,静::静态方法
* 我们都说:引用方法的,参数和返回值,必须和抽象方法的参数和返回值一一对应,
*
* 演示特殊的情况: 类::非静态方法
* 非静态方法的参数和返回值,和抽象方法不一致,也不影响使用方法引用!!
*
* 例子如下:
*/
@Test
public void test3(){
/**
* 1. Object类的toString()
* String toString()
* 2.函数式接口(Function)的抽象方法
* R apply(T t);
*
* 很明显:toString方法和apply()抽象方法,参数不对应
* 那么这时apply(T t)抽象方法的形参t,就会作为调用者,来调用toString()
*
* 总结: 抽象方法单参有返回 --通过类::实例方法--> 无参有返回!
*/
String s1 = Arrays.asList("aa", "bb").stream().map(new Function<String, String>() {
@Override
public String apply(String s) {
return s.toString();
}
}).collect(Collectors.joining("+"));
System.out.println(s1);
String s2 = EmployeeData.getEmployees().stream().map(Object::toString).collect(Collectors.joining("+"));
System.out.println(s2);//Employee(id=1001, name=wzj, age=22, salary=50000.0)+Employee(id=1002, name=tom, age=22, salary=40000.0) 我只用了集合中的两个元素演示一下!
}
/**
* 1.String类中的实例方法
* int compareTo(String anotherString)
* 2.函数式接口(Comparator)的抽象方法
* int compare(String o1, String o2)
*
* 很明显:compareTo方法和compare()抽象方法,参数不对应
* 那么这时抽象方法compare中的参数o1,就会作为调用者来调用方法compareTo,将参数2,o2作为形参传进去!!
*
* 总结:抽象方法多参有返回 --通过类::实例方法--> 单参有返回!!
*/
@Test
public void test4(){
Comparator<String> c1 = new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o1.compareTo(o2);
}
};
System.out.println(c1.compare("1","2")); // -1
//演示方法引用之,类::实例方法
Comparator<String> c2 = String::compareTo;
System.out.println(c2.compare("2","1")); // 1
}
2、构造器引用
@Data
class Employee {
private int id;
private String name;
public Employee() {}
public Employee(int id) {
this.id = id;
}
public Employee(int id, String name) {
this.id = id;
this.name = name;
}
}
/**
* 构造器引用:
* 和方法引用类似
* 函数式接口的抽象方法的形参列表,和构造器的形参列表一致
* 抽象方法的返回值类型,即为构造器所属的类的类型!
*
* 数组引用!
* 这里可以将数组引用看做成一个特殊的类,则写法和构造器引用就一致了
*
* 使用场景;就是一个抽象方法的返回值类型是一个类就可以用构造器引用,是一个数组就用数组引用!
*
* 总结:lambda表达式是函数式接口的实例,其实方法引用,构造器引用可以替换lambda表达式,所以它们也是函数式接口的实例!
* 方法引用,构造器引用不会-->就写lambda表达式,lambda不会--->就写原生的
*/
public class ConstructorRefTest {
/**
* 构造器引用之无参构造
* @FunctionalInterface
* Supplierr<T>{
* T get();
* }
*/
@Test
public void test3(){
// 1.匿名内部类
Supplier<Employee> s1 = new Supplier<Employee>() {
@Override
public Employee get() {
return new Employee();
}
};
System.out.println(s1.get()); //Employee(id=0, name=null)
// 2.lambda表达式
Supplier<Employee> s2 = () -> new Employee();
System.out.println(s2.get()); //Employee(id=0, name=null)
// 3.构造器引用
Supplier<Employee> s3 = Employee::new;
System.out.println(s3.get()); //Employee(id=0, name=null)
}
/**
* 构造器引用之单参构造
* @FunctionalInterface
* Function<T,R>{
* R apply(T);
* }
*/
@Test
public void test4(){
// 1.匿名内部类
Function<Integer,Employee> fun1 = new Function<Integer, Employee>() {
@Override
public Employee apply(Integer id) {
return new Employee(id);
}
};
System.out.println(fun1.apply(007)); //Employee(id=7, name=null)
// 2.lambda表达式
Function<Integer,Employee> fun2 = id -> new Employee(id);
System.out.println(fun2.apply(007)); //Employee(id=7, name=null)
//3. 构造器引用
Function<Integer,Employee> fun3 = Employee::new;
System.out.println(fun3.apply(007)); //Employee(id=7, name=null)
}
/**
* 构造器引用之多参构造
* @FunctionalInterface
* public interface BiFunction<T, U, R> {
* R apply(T t, U u);
* }
*/
@Test
public void test5(){
// 1.匿名内部类
BiFunction<Integer,String,Employee> b1 = new BiFunction<Integer, String, Employee>() {
@Override
public Employee apply(Integer id, String name) {
return new Employee(id,name);
}
};
System.out.println(b1.apply(007,"wzj")); //Employee(id=7, name=wzj)
// 2.lambda表达式
BiFunction<Integer,String,Employee> b2 = (id,name) -> new Employee(id,name);
System.out.println(b2.apply(007,"wzj")); //Employee(id=7, name=wzj)
// 3.构造器引用
BiFunction<Integer,String,Employee> b3 = Employee::new;
System.out.println(b3.apply(007,"wzj")); //Employee(id=7, name=wzj)
}
}
3、数组引用:
/**
* 数组引用
* Function中的 R apply(T t)
*
* 1.先看接口的泛型,并指定好
* 2.然后再接口中该抽象方法需要传入的泛型是哪个,用一个参数代替就行
*/
@Test
public void test1(){
// 1.匿名内部类
Function<Integer,String[]> fun1 = new Function<Integer, String[]>() {
@Override
public String[] apply(Integer i) {
return new String[i];
}
};
System.out.println(Arrays.toString(fun1.apply(3))); // [null, null, null]
// 2.lambda表达式
Function<Integer,String[]> fun2 = (i) -> new String[i];
System.out.println(Arrays.toString(fun2.apply(3))); // [null, null, null]
//3.数组引用
//注: 由于函数式接口中的抽象方法的返回值是数组,所以这里使用了数组引用
Function<Integer,String[]> fun3 = String[]::new;
//注:数组不能直接使用foreach()遍历,需要先转换成流然后调用foreach()
Stream<String> strStream = Arrays.stream(fun2.apply(3));
//注: System.out::prinln是方法引用(对象::非静态方法)
strStream.forEach(System.out::println); //null,null,null 将元素一个个打印出来了!
}
/**
* 需求:从集合中筛选姓“张”的存入到数组中 !!
*/
@Test
public void test2(){
// 1.创建集合
List<String> strList = new ArrayList<>(Arrays.asList("张三","张飞","李四"));
// 2.将集合转换成流, 因为Collection接口中有stream()方法!
/**
* 下面调用的是带参的toArray()
* <A> A[] toArray(IntFunction<A[]> generator);
*
* 函数式接口
* IntFunction<R>{
* R apply(int value);
* }
*
* 抽象方法单参有返回: R apply(int value);
* 数组引用: 无参有返回, 只将创建的数组作为返回值返回了!, 神奇的是元素还已经装进了数组中!
*/
String[] ss = strList.stream().filter(str -> str.startsWith("张")).toArray(String[]::new);
System.out.println(Arrays.toString(ss)); //[张三, 张飞]
}
总结
1.不管是原生的匿名内部类,还是lambda表达式,还是方法引用,构造器引用,数组引用.本质上都是函数式接口的实现类对象!
2.构造器引用和数组引用的使用场景是一个抽象方法的返回值是一个类/数组,此时可以使用
3.构造器引用(Employee::new)和数组引用(String[]::new)的使用语法差不多,可以反过来看不就是new Employee, new String[]
4.一般来说如果想使用方法引用,那么必须保证函数式接口中的抽象方法的参数和返回值的个数和类型,与方法引用的参数和返回值的个数和类型应该保持一一对应!
5.但是注意有特殊的!!比如:方法引用(类::实例方法),构造器引用,数组引用. 它们的参数和返回值个数和类型与抽象方法的参数和返回值的个数和类型没有保持一般,仍然可以调用
比如:
①: 函数式接口中的抽象方法假如是单参有返回值,使用类::实例方法,构造器引用,数组 就可以做到无参有返回值
②: 函数式接口中的抽象方法假如是多参有返回值,使用类::实例方法,构造器引用,数组 就可以做到单参有返回值
来自:虽然帅,但是菜的cxy,每天进步一点点 peace