zoukankan      html  css  js  c++  java
  • java lambda表达式和函数式接口使用示例

    
    import java.util.Arrays;
    import java.util.Collections;
    import java.util.Comparator;
    import java.util.List;
    import java.util.function.Consumer;
    
    import org.junit.Test;
    
    /**
     * @author gm
     * Lambda  表达式的基础语法:java8中引入一个新的操作符 "->" ,该操作符称为箭头操作符或lambda操作符
     *      箭头操作符将lambda拆分成两部分:
     *      左侧:lambda表达式的参数列表
     *      右侧:lambda表达式中所需执行的功能,即lambda体
     *  语法格式一:无参数,无返回值
     *      () -> System.out.println("xxxxxx");
     *  语法格式二:有一个参数,无返回值
     *      (x) -> System.out.println(x);
     *  语法格式三:若只有一个参数,小括号可以省略不写
     *      x -> System.out.println(x);
     *  语法格式四:有两个以上的参数,有返回值,并且lambda体中有多条语句    test4
     *      Comparator<Integer> comparator = (x,y) -> {
     *            System.out.println("函数式接口");
     *            return Integer.compare(x, y);
     *        };
     *    语法格式五:若lambda体中只有一条语句,则return和大括号都可以省略不写
     *        Comparator<Integer> comparator = (x,y) -> Integer.compare(x, y);
     *    语法格式六:lambda表达式的参数列表的数据类型可以省略不写,因为jvm编译器可以根据上下文推断出数据类型,即“类型推断”
     *        (Integer x,Integer y) -> Integer.compare(x, y);  == (x,y) -> Integer.compare(x, y);
     *    
     *    左右遇一括号省(左边是一个参数或者右边只有一条语句), 左侧推断类型省(左边不需要显示指定类型)
     *
     *    二、lambda表达式需要函数式接口的支持
     *        函数式接口:接口中只有一个抽象方法的接口(这样才知道要动态替换哪个方法),可以使用 @FunctionalInterface 检查一下
     *        反而言之:jdk接口上有@FunctionalInterface注解的都是函数式接口
     */
    public class TestLambda {
        
    	@Test
    	public void test1() {
    		new Thread(new Runnable() {
    			@Override
    			public void run() {
    				System.out.println("hello lambda1");
    			}
    		}).start();
    
    		// 相当于实现接口Runable无参方法run的匿名实现类.这里的实现同上面的匿名类效果一样
    		Runnable r1 = () -> System.out.println("hello lambda2");
    		new Thread(r1).start();
    	}
    	
    	// Consumer接口 accept该函数式接口的唯一的抽象方法,接收一个参数,没有返回值
    	@Test
    	public void test2() {
    		// 这里因为是一个参数,所以左边的括号省略,右边是一个表达式,所以右边的大括号省略
    		// Consumer是一个消费者型的函数式接口,其accept方法可以对接收到的数据进行处理,无返回值。这里相当于实现其抽象方法accept
    		Consumer<String> consumer = x -> System.out.println(x);
    		consumer.accept("我很帅");
    	}
    	
    	// Consumer接口andThen方法,在执行完调用者方法后再执行传入参数的方法
    	@Test
    	public void test3() {
    		// 这里因为是一个参数,所以左边的括号省略,右边是一个表达式,所以右边的大括号省略
    		// Consumer是一个消费者型的函数式接口,其andThen方法在执行完调用者方法后再执行传入参数的方法
    		Consumer<String> consumer = x -> System.out.println(x);
    		Consumer<String> consumer2 = x -> System.out.println("是真的,大家都是这么觉得!");
    		consumer.andThen(consumer2).accept("我很帅是吗?");
    	}
    
    	@Test
    	public void test4() {
    		// 这里左边是两个参数,所以使用括号,右边是两条语句,使用大括号。这里是实现了Comparator接口的compare方法,用于collection排序操作
    		Comparator<Integer> comparator = (x, y) -> {
    			System.out.println("函数式接口");
    			return Integer.compare(x, y);
    		};
    		// 这里是简写,效果是倒序
    		Comparator<Integer> comparator2 = (x, y) -> Integer.compare(y, x);
    
    		Integer[] array = { 1, 9, 6, 7, 3, 2, 8 };
    		List<Integer> list = Arrays.asList(array);
    		list.sort(comparator);
    		System.out.println(list);
    		list.sort(comparator2);
    		System.out.println(list);
    	}
    
    	// 通过这里的两个lambda实现,可以发现函数式接口的方法是动态改变的,而且不用继续接口,不用匿名类,实现起来方便快捷
    	@Test
    	public void test5() {
    		// (x) -> (x + 1)是一个lambda表达式,功能是自增。
    		// 其相当于一个入参和返回值类型相同的函数,这里将其传给MyFun<Integer>,可以作为函数式接口MyFun内方法getValue的实现。
    		// 可以理解为MyFun内方法getValue的实现变成了整数值自增然后返回
    		Integer result = operation(100, (x) -> (x + 1));
    		// 这里输出101
    		System.out.println(result);
    		// 这里同理,只是getValue的实现变成了自减,所以输出结果为99
    		System.out.println(operation(100, (x) -> (x - 1)));
    	}
    
    	public Integer operation(Integer num, MyFun<Integer> mf) {
    		return mf.getValue(num);
    	}
    
    	List<User> users = Arrays.asList(new User("zhangsan", 24, 7500), new User("lisi", 25, 13000), new User("wangwu", 26, 20000));
    
    	@Test
    	public void test6() {
    		// 这里第二个参数是lambda表达式,其实现了函数表达式式Comparator的compare方法
    		Collections.sort(users, (u1, u2) -> {
    			return u1.getAge() - u2.getAge();
    		});
    		System.out.println(users);
    	}
    
    	@Test
    	// 对两个long型进行处理
    	public void test7() {
    		// 参数3是lambda,用于实现函数式接口。相当于MyFun2的getValue(a,b)功能变成了a+b
    		op(100L, 200L, (x, y) -> x + y);
    	}
    
    	public void op(Long t1, Long t2, MyFun2<Long, Long> mf2) {
    		System.out.println(mf2.getValue(t1, t2));
    	}
    
    	@Test
    	public void test8() {
    		// 这里是stream配合lambda表达式一起使用。stream这里简单理解为遍历list
    		// (e) -> e.getSalary() >= 10000是函数式接口Predicate内方法test的实现,其功能是判断是否正确
    		// 下面这里就是判断list中的元素的salary是否大于10000,大于的继续往下处理
    		// forEach就是遍历打印。这里总体的功能就是遍历list,打印salary大于10000的User
    		users.stream().filter((e) -> e.getSalary() >= 10000).forEach(System.out::println);
    		users.stream().map((e) -> e.getName()).forEach(System.out::println);
    	}
    }
    
    
    @FunctionalInterface
    public interface MyFun<T> {
    	public T getValue(T value);
    }
    
    @FunctionalInterface
    public interface MyFun2<T, R> {
    
    	public R getValue(T t1, T t2);
    }
    
    
    public class User {
    	private String name;
    	private int age;
    	private int salary;
    
    	public User() {
    	}
    
    	public User(String name, int age, int salary) {
    		super();
    		this.name = name;
    		this.age = age;
    		this.salary = salary;
    	}
    
    	public String getName() {
    		return name;
    	}
    
    	public void setName(String name) {
    		this.name = name;
    	}
    
    	public int getAge() {
    		return age;
    	}
    
    	public void setAge(int age) {
    		this.age = age;
    	}
    
    	public int getSalary() {
    		return salary;
    	}
    
    	public void setSalary(int salary) {
    		this.salary = salary;
    	}
    
    }
    
    import java.util.ArrayList;
    import java.util.Arrays;
    import java.util.List;
    import java.util.function.Consumer;
    import java.util.function.Function;
    import java.util.function.Predicate;
    import java.util.function.Supplier;
    import org.junit.Test;
    
    /**
     * @author gm
     * java8 内置的四大核心函数式接口
     * 
     * Consumer<T>: 消费型接口,接收数据并处理
     *         void accept(T t);
     * Supplier<T>: 供给型接口,对外提供数据
     *         T get()
     * Function<T, R>: 函数型接口,接收参数,返回结果
     *         R apply(T t);
     * Predicate<T>: 断言型接口,检测入参是否符合条件(符合则返回true)
     *         boolean test(T t);
     *
     */
    public class TestLambdaFunc {
    
    	@Test
    	// Consumer<T> 消费型接口
    	public void testConsumer() {
    		// Consumer是消费型接口,其可定义对接收到的数据进行不同的处理。这里的处理方式就是打印详细信息。
    		// m -> System.out.println("工资:" + m + " 元") 可以理解为Consumer中accept(T t)方法的实现
    		// m -> System.out.println("这" + m + " 元真,是辛苦所得") 执行完调用者方法后再执行传入参数的方法
    		happy(10000L, m -> System.out.println("工资:" + m + " 元"), m -> System.out.println("这" + m + " 元真,是辛苦所得"));
    	}
    
    	public void happy(double money, Consumer<Double> con, Consumer<Double> con2) {
    		con.andThen(con2).accept(money);
    	}
        
    	// Supplier<T> 供给型接口
    	@Test
    	public void testSupplier() {
    		// Supplier是供给型接口。其内部定义对外输出的数据。而且不需要入参
    		// () -> (int)(Math.random() * 100) 为这里的供给行为,即返回一个随机数
    		List<Integer> numList = getNumList(10, () -> (int) (Math.random() * 100));
    		System.out.println(numList);
    	}
        
    	public List<Integer> getNumList(int num, Supplier<Integer> sup) {
    		List<Integer> list = new ArrayList<>();
    
    		for (int i = 0; i < num; i++) {
    			Integer n = sup.get();
    			list.add(n);
    		}
    		return list;
    	}
        
    	// Function<T,R> 函数型接口
    	@Test
    	public void testFunction() {
    		// 函数型接口功能相对强大,可以对接收到的数据进行进一步处理,返回类型可以和入参类型不一致(泛型接口,二元组)
    		// (x) -> (x + ", 哈哈哈") 这里作为Function接口中apply的实现
    		String str = strHandler("我最帅", (x) -> (x + ", 哈哈哈"));
    		System.out.println(str);
    	}
    
    	private String strHandler(String str, Function<String, String> fun) {
    		return fun.apply(str);
    	}
        
    	// Predicate<T> 断言型接口
    	@Test
    	public void testPredicate() {
    		List<String> list = Arrays.asList("Hello", "World", "www.baidu.com");
    		// 函数式接口Predicate主要用于判断,x -> (x.length() > 5) 这里是判断入参的长度是否大于5
    		List<String> filterStr = filterStr(list, x -> (x.length() > 5));
    		System.out.println(filterStr);
    	}
    
    	public List<String> filterStr(List<String> list, Predicate<String> pre) {
    		List<String> strList = new ArrayList<>();
    
    		for (String str : list) {
    			if (pre.test(str)) {
    				strList.add(str);
    			}
    		}
    		return strList;
    	}
    }
    
    
    import java.io.PrintStream;
    import java.util.Arrays;
    import java.util.Comparator;
    import java.util.List;
    import java.util.function.BiPredicate;
    import java.util.function.Consumer;
    import java.util.function.Supplier;
    
    import org.junit.Test;
    
    /**
     * @author gm
     * 方法引用:若lambda体中的内容有方法已经实现了,我们可以使用“方法引用”
     *         (可以理解为方法引用是lambda表达式的另外一种表达形式)
     * 主要有三种语法格式:
     *         对象::实例方法名
     *         类::静态方法名
     *         类::实例方法名
     * 注意:
     *     1.lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的参数列表和返回值类型一致
     *  2.若lambda参数列表 中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用className::method
     *  
     *  二、构造器引用
     *  格式:
     *      ClassName::new
     */
    public class TestMethodRef {
    
    	// 对象::实例方法名
    	@Test
    	public void test() {
    		Consumer<String> consuemr = (x) -> System.out.println(x);
    		consuemr.accept("aaaa");
    
    		PrintStream ps = System.out;
    		Consumer<String> consumer2 = ps::println;
    		consumer2.accept("bbbb");
    	}
        
    	@Test
    	public void test2() {
    		User user = new User("gm", 30, 200000);
    		// 这里是无参,所以左边使用了()
    		Supplier<String> sup = () -> user.getName();
    		System.out.println(sup.get());
    
    		// 这里对lambda表达式进行了省略。() -> user.getAge() == user::getAge
    		Supplier<Integer> sup2 = user::getAge;
    		System.out.println(sup2.get());
    	}
        
    	// 类::静态方法名
    	@Test
    	public void test3() {
    		Comparator<Integer> com = (x, y) -> Integer.compare(y, x);
    
    		// 这里因为入参和lambda实现方法要调用的入参一样。所以两边都省略了
    		Comparator<Integer> com1 = Integer::compare;
    
    		Integer[] array = { 1, 9, 6, 7, 3, 2, 8 };
    		List<Integer> list = Arrays.asList(array);
    		list.sort(com);
    		System.out.println(list);
    
    		list.sort(com1);
    		System.out.println(list);
    	}
        
    	// 类::实例方法名
    	@Test
    	public void test4() {
    		BiPredicate<String, String> bp = (x, y) -> x.equals(y);
    
    		// 这里是两个入参,而且满足第一个参数是新方法调用者,第二个参数是入参的情况
    		// 理论而言都用上面的表达式即可,看起来比较简单,但是不能避免别人不会使用简写方式,看不懂岂不是很尴尬
    		BiPredicate<String, String> bp2 = String::equals;
    		System.out.println(bp.test("1", "1"));
    		System.out.println(bp2.test("Hello", "World"));
    	}
    
    	@Test
    	public void test5() {
    		// 函数式接口生产数据的方式是new User();
    		// 创建Supplier容器,声明为User类型,此时并不会调用对象的构造方法,即不会创建对象
    		Supplier<User> sup = () -> new User();
    		// 这里功能同上,简写方式
    		Supplier<User> sup2 = User::new;
    		// 调用get()方法,此时会调用对象的构造方法,即获得到真正对象
    		System.out.println(sup.get());
    		// 每次get都会调用构造方法,即获取的对象不同
    		System.out.println(sup2.get());
    	}
    }
    
    
  • 相关阅读:
    swift把颜色转成图片
    第四篇:断路器(Hystrix)
    第三篇: 服务消费者(Feign)
    第二篇:服务消费者(RestTemplate+ribbon)
    第一篇:服务的注册与发现Eureka(Finchley版本)
    递归打印目录层次(java版)
    zuul熔断代码
    Window安装Erlang环境
    移动一根火柴使等式成立js版本(递归)
    mysql 存储过程 游标嵌套
  • 原文地址:https://www.cnblogs.com/gmhappy/p/13457031.html
Copyright © 2011-2022 走看看