Java8新特性【方法引用】与【构造器引用】详细讲解

Lambda后传之Java8新特性【方法引用】与【构造器引用】

在此之前建议大家了解《Lambda表达式》《Java8的四大函数接口》之后再来看

一.方法引用:

如果lambda表达式中有方法已经实现了,就可以使用【方法引用】。也可以理解为【方法引用】是Lambda表达式的另外一种表现形式。
它的基本思想是,如果一个Lambda代表的只是“直接调用这个方法”,那最好还是用名称 来调用它,而不是去描述如何调用它。事实上,方法引用就是让你根据已有的方法实现来创建 Lambda表达式。但是,显式地指明方法的名称,你的代码的可读性会更好。

1.表达形式

1.对象::实例方法名
2.类::静态方法名
3.类::实例方法名

注释:

实例方法:属于对象的方法,由对象来调用。
静态方法:使用static修饰(静态方法),属于整个类的,不是属于某个实例的,只能处理static域或调用static方法;

2.案例分析

1⃣️:对象::实例方法名

//consumer函数就是对所输入的参数进行操作,这里是打印出所输入的参数hhh
Consumer<String> con = x -> System.out.println(x);
con.accept("hhh");

因为Lambda体中已经有println()方法完成了我们想要的功能,因此可以用方法引用的方式表达
Lambda体中System.out.println(x);其实是PrintStream out = System.out;out.println();两步操作完成的,因此println()方法是一个实例方法,对象名为out
应用【方法引用】后:

PrintStream out = System.out; 
Consumer<String> con1 = out::println();
con1.accept("hhh");

最终简化版:

Consumer<String> con2 =System.out::println();
con2.accept("hhh");

注意⚠️:要实现的接口中的抽象方法的参数列表和返回值类型,必须要与调用的方法中的参数列表和返回值类型保持一致。
上述例子中:接口中的抽象方法为 void accept(T t); 调用的方法为 public void println(String x)

再比如,我有一个Student类,定义了name和age两个私有属性,有get和set方法。想要获取stu的年龄,通过supplier函数

        Student stu = new Student("xiaowang",20);
		Supplier<Integer> sup = ()->sup.getAge();
		Integer num = sup.get();
		System.out.println(num);

可以简化为:

     Student stu = new Student("xiaowang",20);
	 Supplier<Integer> sup =stu::getAge;
	 Integer num = sup.get();
    System.out.println(num);

也可以:

 Supplier<Integer> sup =Student::getAge;

因为 Supplier< Integer> sup =(Student stu)-> stu.getAge();


2⃣️:类::静态方法名

Comparator<Integer> cam1 = (x, y) -> Integer.compare(x, y);
System.out.println(cam1.compare(3, 2));

因为函数接口中抽象方法:int compare(T o1, T o2);与调用方法:public static int compare(int x, int y)返回值类型与参数类型一致,并且调用方法为静态方法,所以应用【方法引用】之后为:

Comparator<Integer> com1 = Integer::compare;
System.out.println(com1.compare(1, 2));

3⃣️:类::实例方法名

a.实例上的实例方法

BiPredicate是Predicate的子类,其区别是,BiPredicate可以传入两个参数进行比较。

@FunctionalInterface
public interface BiPredicate<T, U> {

    /**
     * Evaluates this predicate on the given arguments.
     *
     * @param t the first input argument
     * @param u the second input argument
     * @return {@code true} if the input arguments match the predicate,
     * otherwise {@code false}
     */
    boolean test(T t, U u);
  }

比如相比较两个字符串

BiPredicate<String, String> bp = (x, y) -> x.equals(y);
System.out.println(bp.test("a", "b"));

使用【方法引用】后:

BiPredicate<String, String> bp1 = String::equals;
System.out.println(bp1.test("a", "b"));

注意⚠️:equals为实例方法,为什么不能用对象::实例方法名的方式,因为当lambda的参数列表中第一个参数为方法的调用者,第二个参数为实例方法的参数时,可以通过类::实例方法名使用【方法引用】

b.类型上的实例方法

例子:

String::toString 等价于lambda表达式 (s) -> s.toString()

这里不太容易理解,实例方法要通过对象来调用,方法引用对应Lambda,Lambda的第一个参数会成为调用实例方法的对象。


二.构造器引用

1.应用格式

类名::new

2.案例分析

1⃣️.应用Java8内置的函数式接口实现

Student类:
其中有一个无参构造器,一个含有一个参数的有参构造器,和一个含有两个参数的有参构造器。

public class Student {	
	
	 private String name;
	 private int age;
	 
	public Student(){}
	
	public Student(int age)
	{
		super();
		this.age = age;
	}
	
	public Student(String name, int age)
	{
		super();
		this.name = name;
		this.age = age;
	}
	
	@Override
	public String toString()
	{
		return "Student [name=" + name + ", age=" + age + "]";
	}
}

1⃣️:没有用构造器引用之前,无参构造

 public class Main {
     public static void main(String[] args) {
     //没有用构造器引用之前,无参构造
     Supplier<Student> sup = () -> new Student();
     Student stu1 = sup.get();
     System.out.println(stu1);
}

Output:

Student [name=null, age=0]

因为用的无参构造器,没有传入参数,所以name为null,age为0;

1⃣️:使用构造器引用之后,无参构造

 public class Main {
     public static void main(String[] args) {
     //用造器引用之后,无参构造
     Supplier<Student> sup = Student::new;
     Student stu1 = sup.get();
     System.out.println(stu1);
}

2⃣️:没有用构造器引用之前,一个参数的有参构造

class Test{
	public static void main(String []args) {
		Function<Integer, Student> fun = (age) -> new Student(age);
	     Student stu1 = fun.apply(18);
	     System.out.println(stu1);
	}
}

Output:

Student [name=null, age=18]

2⃣️:使用构造器引用之后,一个参数的有参构造

class Test{
	public static void main(String []args) {
		Function<Integer, Student> fun = Student::new;
	     Student stu1 = fun.apply(18);
	     System.out.println(stu1);
	}
}

3⃣️:未使用构造器引用之前,两个参数的有参构造

class Test{
	public static void main(String []args) {
		BiFunction<String,Integer, Student> fun = (name,age) ->new Student(name,age);
	     Student stu1 = fun.apply("xiaowang",18);
	     System.out.println(stu1);
	}
}

Output:

Student [name=xiaowang, age=18]

3⃣️:使用构造器引用之后,两个参数的有参构造

class Test{
	public static void main(String []args) {
		BiFunction<String,Integer,Student> fun = Student::new;
	     Student stu1 = fun.apply("xiaowang",18);
	     System.out.println(stu1);
	}
}

三种构造引用:

Supplier<Student> sup = Student::new;
Function<Integer, Student> fun = Student::new;
BiFunction<String,Integer, Student> fun = Student::new;

注意⚠️:

  • 构造引用中,等号后面统一格式类名::new
  • 构造引用中,创建对象时我们需要传入的参数都在接口中

比如,我们看一下BiFunction接口的源码:

@FunctionalInterface
public interface BiFunction<T, U, R> { 

 /**
     * Applies this function to the given arguments.
     *
     * @param t the first function argument
     * @param u the second function argument
     * @return the function result
     */
    R apply(T t, U u);
    
    }
  • 在BiFunction<T, U, R>接口中,我们需要三个参数T,U,R,与上文例子对应,T就是String,U就是Integer,R就是Student。
  • 在接口的R apply(T t, U u);方法中,我们可以接受两个分别参数,分别为T类型的tU类型的u,并且返回一个参数R。与上文例子对应,t就是String类型的name,U就是Integer类型的age,R就是Student。

2⃣️.应用自定义接口实现

当然我们也可以自定义接口

@FunctionalInterface 
 interface MyInterface<S>{
      public S show(String n,int a);
  }
  • 在MyInterface< S>接口中,我们需要一个参数S,在下文中S就是Student。
  • 在接口的S show(String n,int a)方法中,我们可以接受两个参数n和a,并返回参数S。其中n就是name,a就是age,S就是Student。
public class Main {
    public static void main(String[] args) {
         MyInterface<Student> m = Student::new;
         Student stu = m.show("xiaowang",20);
         System.out.println(stu);
     }
 }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值