JDK新特性--lambda

1.lambda表达式

1.格式:
(参数类型 参数名称) ->{
方法体;
    return 返回值;
}

2.省略规则
小括号中的参数类型可以省略。
如果小括号中只有一个参数,那么可以省略小括号。
如果大括号中只有一条语句,那么可以同时省略大括号、return关键字及语句分号。
eg:
new Thread(() ->{
            System.out.println("Lambda表达式执行了");
        }).start();
方法体:
可以是表达式也可以代码块,是函数式接口里方法的实现
如果负责运算的代码无法用表达式表示,可以使用编写方法实现
但必须用{}包围并按需明确使用 return语句

2.函数式接口分类 

a.系统与定义函数接口(Comparator, Runnable)

b.用户自定义函数接口(注解必须有,表达式是直接通过参数列表来实现的,只能有一个有效方法)

@FunctionalInterface
public interface MyInterface {
    String info(String tip);
}

 3.公共定义的函数式接口

a.功能性接口:Function<T, R>

有输入参数,有返回值

是对接收一个T类型参数,返回R类型的结果的方法的抽象

通过调用apply方法执行内容

需求:给定一个字符串,返回字符串长度

 

package org.xxxx.demo01;
 
import java.util.function.Function;
 
public class Demo01 {
	public static void main(String[] args) {
		// 定义字符串
		String str = "helloworld";
		
		// 调用方法
		// 在调用的时候写方法体,方法比较灵活
		int length = testFun(str, (s) -> s.length());
		
		System.out.println(length);
	}
	
	// 方法
	/**
	 * 
	 * @param str 输入参数
	 * @param fun 表达式 String 为输入类型,Integer为输出类型
	 * @return 返回字符串长度
	 */
	public static int testFun(String str, Function<String, Integer> fun) {
		// 执行
		Integer length = fun.apply(str);
		return length;
	}
}

b.消费型接口:Consumer<T>

有输入参数,没返回值
对应的方法类型为接收一个参数,没有返回值
一般来说使用Consumer接口往往伴随着一些期望状态的改变
或者事件的发生,典型的forEach就是使用的Consumer接口
虽然没有任何的返回值,但是向控制台输出结果
Consumer 使用accept对参数执行行为

package org.xxxx.demo01;
 
import java.util.function.Consumer;
 
public class Demo01 {
	public static void main(String[] args) {
		// 创建字符串
		String str = "hello world";
		
		// 调用
		testCon(str, (s) -> System.out.println(s));
	}
	
	/**
	 * 
	 * @param str 传入参数
	 * @param con
	 */
	public static void testCon(String str, Consumer<String> con) {
		// 执行
		con.accept(str);
	}
}

c.供给型接口:Supplier<T>

无传入参数,有返回值

该接口对应的方法类型不接受参数,但是提供一个返回值

使用get()方法获得这个返回值

package org.xxxx.demo01;
 
import java.util.function.Supplier;
 
public class Demo01 {
	public static void main(String[] args) {
		// 创建字符串
		String str = "hello world";
		
		// 调用
		String sup = testSup(() -> str);
		
		System.out.println(sup);
	}
	
	/**
	 * 
	 * @param sup
	 * @return
	 */
	public static String testSup(Supplier<String> sup) {
		// 执行
		String s = sup.get();
		return s;
	}
	
}

d.断言型接口:Predicate<T>

有传入参数,有返回值Boolean

该接口对应的方法为接收一个参数,返回一个Boolean类型值

多用于判断与过滤,使用test()方法执行这段行为

需求:输入字符串,判断长度是否大于0


package org.xxxx.demo01;
 
import java.util.function.Predicate;
 
public class Demo01 {
	public static void main(String[] args) {
		// 创建字符串
		String str = "hello world";
		
		// 调用
		boolean flag = testPre(str, (s) -> s.length() > 0);
		
		System.out.println(flag);
	}
	
	/**
	 * 
	 * @param str
	 * @param pre
	 * @return
	 */
	public static boolean testPre(String str, Predicate<String> pre) {
		// 执行
		boolean flag = pre.test(str);
		
		return flag;
	}
	
}

3.Lambda 和 匿名内部类对比

1.所需的类型不一样

     匿名内部类,需要的类型可以使类,抽象类,接口;

     Lambda表达式,需要的类型必须是接口。

2.抽象方法的数量不一样

     匿名内部类所需的接口中抽象方法的数量随意;

     Lambda表达式所需的接口只能有一个抽象方法。

3.实现原理不同

     匿名内部类是在编译后,会形成额外的一个 类名$0 的.class文件

     Lambda 表达式实在程序运行的时候动态生成 .class 文件

4.例子

public class CollectionsLambdaDemo{
 
    public static void main(String[] args) {
        List<Person> persons = new ArrayList<>();
        persons.add(new Person("刘德华",58,174));
        persons.add(new Person("张学友",56,176));
        persons.add(new Person("郭富城",54,171));
        persons.add(new Person("黎明",53,178));
 
        //1.匿名内部类,对集合进行排序
        Collections.sort(persons, new Comparator<Person>() {
            //年龄降序排序
            @Override
            public int compare(Person o1, Person o2) {
                return o2.getAge() - o1.getAge();
            }
        });
 
        for (Person person: persons) {
            System.out.println(person);
        }
 
        System.out.println("-----------------------------------");
 
        //2.Lambda表达式
        Collections.sort(persons,(Person o1,Person o2) ->{
            return o1.getAge() - o2.getAge();
        });
 
         for (Person person: persons) {
             System.out.println(person);
         }
    }
}
 
//测试结果:
    Person{name='刘德华', age=58, height=174}
    Person{name='张学友', age=56, height=176}
    Person{name='郭富城', age=54, height=171}
    Person{name='黎明', age=53, height=178}
    -----------------------------------
    Person{name='黎明', age=53, height=178}
    Person{name='郭富城', age=54, height=171}
    Person{name='张学友', age=56, height=176}
    Person{name='刘德华', age=58, height=174}

5.方法引用

5.1格式

符号表示:双冒号(::)

符号说明:双冒号为方法引用运算符,而它所在的表达式被称为方法引用。

应用场景:如果 Lambda 所要实现的方案,已经在其他方法存在相同方案,那么则可以使用方法引用。

 5.2 常见引用方式

方法引用在 JDK8 中使用方式相当灵活,有如下几种形式:

 1.instanceName::methodName    对象名::方法名

 2.ClassName::staticMethodName    类名::静态方法名

 3.ClassName::methodName    类名::方法名

 4.ClassName::new    类名::new   

 5.TypeName[ ]::new    数组类型::new  (String[]::new)

 5.2.1 对象名::方法名

public class InstanceName_methodName_demo {
 
    public static void main(String[] args) {
        Date now = new Date();
 
        //Lambda 普通写法(调用方法)
        Supplier<Long> supplier1 = ()->{
            return now.getTime();
        };
        Long aLong = supplier1.get();
        System.out.println("普通写法:"+aLong);
 
        //Lamdba 方法引用(对象名::成员方法名)
        Supplier<Long> supplier2 = now::getTime;
        Long bLong = supplier2.get();
        System.out.println("方法引用写法:"+bLong);
 
        //使用 方法引用 来修改Time
        Consumer<Long> consumer = now::setTime;
        consumer.accept(100000000000L);
        Supplier<Long> supplier = now::getTime;
        Long aLong1 = supplier.get();
        System.out.println("修改后的Time:"+aLong1);
 
    }
}

   注意事项:

被引用的方法,参数要和接口中抽象方法的参数一样;(即:getTime() 方法无参,Supplier接口抽象方法get()方法也无参)
当接口抽象方法有返回值时,被引用的方法也必须有返回值。      

5.2.2.类名::静态方法名

 由于在 java.lang.System 类中已经存在了静态方法 currentTimeMillis(),
 所以当我们需要通过 Lambda 表达式来调用该方法时,可以使用方法引用。
 通过类名::静态方法名的方式来调用
 
 public class className_abstractMethodName_demo {
 
    public static void main(String[] args) {
 
        //Lambda普通写法(类名.静态方法名)
        Supplier<Long> supplier1 = ()->{
            return System.currentTimeMillis();
        };
 
        Long aLong = supplier1.get();
        System.out.println("普通调用:"+aLong);
 
        //Lambda 方法引用
        Supplier<Long> supplier2 = System::currentTimeMillis;
        Long bLong = supplier2.get();
        System.out.println("方法引用:"+bLong);
    }
}
 

5.2.3类名::方法名

     在Java面向对象中,类名是只能调用静态方法的。此处使用类名::方法名是有前提的,实际上是拿第一个参数作为方法的调用者。看如下代码先了解一下你就明白了。
     
     public class className_methodName_demo {
 
    public static void main(String[] args) {
        
        //Lambda 表达式(使用 对象.方法名 形式调用)
        Function<String,Integer> fn1 = (String str)->{
            return str.length();
        };
        Integer aLength = fn1.apply("hello world");
        System.out.println("字符长度为:"+aLength);
        
        //Lambda 表达式(使用 方法引用【类名::方法名】方式调用)
        Function<String,Integer> fn2 = String::length;
        Integer bLength = fn2 .apply("hello world"); 
        System.out.println("字符长度为:"+bLength);
 
    }
}

5.2.4 例子

/**
 * TODO 实例:自定义方法 (由繁到简)一步步分析
 *      从 1.Lambda表达式  ---> 2.对象名::方法名 --->  3.类名::方法名 形式 一步步分析
 *
 * @author liuzebiao
 * @Date 2020-1-6 15:38
 */
public class Demo01 {
    
    //数组求和方法
    public int getSum(int[] arr){
        int sum = 0;
        for(int n : arr){
            sum += n;
        }
        return sum;
    }
 
    public static void main(String[] args) {
        //自定义一个数组求和方法 getSum(),本例使用【类名::方法名形式】方法引用方式
        //分析:调用getSum()方法,传入一个 int[]类型参数,返回一个Integer值,此处用到 Function<int[],Integer> 函数式接口
 
        //步骤:
        //1.这种方式显然可以,但是Lambda表达式是冗余的
        Demo01 demo = new Demo01();
        Function<int[],Integer> f1 = (int[] arr)->{
            return demo.getSum(arr);
        };
 
        //2.我们可以使用方法引用   demo::getSum;【对象名::方法名】
        Function<int[],Integer> f2 = demo::getSum;
 
        //3.上面2中使用的是 对象名::成员方法,自己写的方法怎么使用【类名::方法名】方式
        // 分析:此时就得用到 BiFunction 函数式接口了。BiFunction 类似于 Function 函数式接口
        // 差别在于参数个数不同:BiFunction 传递2个参数,Function传递1个参数
 
        // 为什么需要传递两个参数呢?
        // 通过刚才图片分析: 2=1.3方法。此处可以理解为【Integer =Demo01.getSum(int[])】这种格式
        // 所以如果使用【类名::方法名】这种方式,第一个参数必须是当前类名,剩下才是传递的参数和返回参数类型
        // 如果分析用2个参数,使用【类名::方法名】这种方式,则需要传3个参数,JDK8帮我们提供了BiFunction/BiConsumer/BiPredicate这3种
 
        BiFunction<Demo01, int[], Integer> f3 = Demo01::getSum;
        int[] arr = {11, 22, 33, 44, 55};
        // 走到此处,调用apply()方法传参时,此处还得传递一个当前类的实例(new Demo01() new实例/Demo01.class.newInstance()反射等方式)
        // 显然还不如直接使用 上面 2 中的【对象名::方法名】这种方式呢。所以【类名::方法名】这种方式很少会用到
        // 所以 BiFunction/BiConsumer/BiPredicate这3种很少用到
        Integer sum = f3.apply(new Demo01(), arr);
        System.out.println(sum);
    }
}

5.2.5 类名::new(构造器引用)

使用前提:

       构造器参数列表要与接口中抽象方法的参数列表一致!
/**
 * TODO person实体类
 *
 * @author liuzebiao
 * @Date 2020-1-7 15:47
 */
public class Person {
 
    private String  name;
 
    private int age;
 
    public Person() {
        System.out.println("执行无参构造方法");
    }
 
    public Person(String name, int age) {
        System.out.println("执行有参构造方法,name:"+name+";age:"+age);
        this.name = name;
        this.age = age;
    }
 
    @Override
    public String toString() {
        return "Person{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}


/**
 * TODO 构造器引入Demo
 *
 * @author liuzebiao
 * @Date 2020-1-7 15:49
 */
public class className_new_constructor {
 
    public static void main(String[] args) {
 
        //1.使用对象无参构造器,创建一个 Person 对象
        //1.1 使用 Lambda 表达式
        Supplier<Person> sup1 = ()->{
            return new Person();
        };
        Person person1 = sup1.get();
        System.out.println(person1);
 
        //1.2 使用方法引用【类名::new(构造器)】方式
        Supplier<Person> sup2 = Person::new;
        Person person2 = sup2.get();
        System.out.println(person2);
 
        //2.使用对象有参构造器,创建一个 Person 对象
        //此种情况会用到 BiFunction 函数式接口(前两个参数对应构造器参数类型,第三个参数为返回值类型)
        //2.1 使用 Lambda 表达式
        BiFunction<String,Integer,Person> bif1 = (String name,Integer age)->{
            return new Person(name,age);
        };
        Person person3 = bif1.apply("Mary", 18);
        System.out.println(person3);
 
        //2.2 使用方法引用【类名::new(构造器)】方式
        BiFunction<String,Integer,Person> bif2 = Person::new;
        Person person4 = bif2.apply("Mary", 18);   //它会根据 apply()方法传入的参数来判断调用哪个构造器
        System.out.println(person4);
    }
}

5.2.6 .数组类型::new(数组引入)

/**
 * TODO 数组引用 创建数组
 *
 * @author liuzebiao
 * @Date 2020-1-7 16:39
 */
public class array_new_demo {
 
    public static void main(String[] args) {
        
        //1.使用 Lambda 表达式,创建一个长度为10的int数组
        Function<Integer,int[]> fn1 = (Integer num)->{
            return new int[num];
        };
        int[] arr1 = fn1.apply(10);
        System.out.println(Arrays.toString(arr1));
 
        //2.使用 数组引用 方式,创建一个长度为10的int数组
        Function<Integer,int[]> fn2 = int[]::new;
        int[] arr2 = fn2.apply(10);
        System.out.println(Arrays.toString(arr2));
    }
}

参考资料来源:JDK8辅助学习(一):Lambda表达式原理分析_扛麻袋的少年的博客-CSDN博客

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值