zoukankan      html  css  js  c++  java
  • Java8 新特性(一)- Lambda 表达式

    2014年3月18日发布了JavaSE 8

    不追求技术的新,追求技术的稳定

    本质:Lambda 表达式是一个匿名函数
    作用:简化代码,增强代码的表达力

    Lambda 语法格式

    // 格式1:无参无返回值
    () -> System.out.println("Hello World!");
    
    // 格式2:有参无返回值
    (x) -> System.out.println(x);
    
    // 格式3:有参有返回值
    (x) -> x * x;
    
    // 格式4:多参有返回值
    (x, y) -> x + y;
    
    // 格式5:函数体包含多条语句
    (x, y) -> {
        System.out.println("加法运算");
        return x + y;
    }
    

    Lambda 表达式中的参数的数据类型可以省略,JVM 编译器能够根据上下文推算出,即“类型推断”

    两个例子

    /** 1. Comparator **/
    TreeSet<Integer> ts1 = new TreeSet<>(new Comparator<Integer>(){
        @Override
        public int compare(Integer i1, Integer i2) {
            return Integer.compare(i1, i2);
        }
    });
    // lambda 表达式
    TreeSet<Integer> ts2 = new TreeSet<>((i1, i2) -> {
        return Integer.compare(i1, i2);
    });
    // 等同于(使用方法引用还可以再次简化)
    TreeSet<Integer> ts3 = new TreeSet<>((i1, i2) -> Integer.compare(i1, i2));
    
    
    /** 2. Runnable */
    Thread t1 = new Thread(new Runnable(){
        @Override
        public void run() {
            System.out.println("当前线程:" + Thread.currentThread().getName());
        }
    });
    // lambda 表达式
    Thread t2 = new Thread(() -> {
        System.out.println("当前线程:" + Thread.currentThread().getName());
    });
    
    

    函数式接口

    !!Lambda 表达式需要函数式接口的支持

    函数式接口:接口只有一个抽象方法

    可以使用注解 @FunctionalInterface 修饰接口,检查是否是函数式接口

    // 定义函数式接口
    @FunctionalInterface
    public interface Calculator<T> {
        public T calculate(T x, T y);
    }
    
    // 使用函数式接口
    Calculator<Integer> calculatorAdd = (x, y) -> x + y;
    Integer result = calculatorAdd.calculate(3, 5);
    

    Java 内置了四大核心函数式接口:

    消费型接口 Consumer<T>:消费一个参数对象

    @FunctionalInterface
    public interface Consumer<T> {
        void accept(T t);
        ...
    }
    

    供给型接口 Supplier<T>:返回一个对象

    @FunctionalInterface
    public interface Supplier<T> {
        T get();
    }
    

    函数型接口 Function<T, R>:传递一个参数,返回一个值

    @FunctionalInterface
    public interface Function<T, R> {
        R apply(T t);
        ...
    }
    

    断定型接口 Predicate<T>:判断参数是否满足约束

    @FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
        ...
    }
    

    对于Java内置函数式接口建议结合 stream 方法理解,在这里了解即可

    除了这4大核心函数式接口外,还有由它们延伸出的一些变种,比如二元消费型接口 BiConsumer<T, U>

    public interface BiConsumer<T, U> {
        void accept(T t, U u);
        ...
    }
    

    方法引用

    将 lambda 体代码封装为方法,然后方法引用,再次简化代码。

    方法引用格式:类名::方法名对象::方法名

    温馨提示:

    实际上,在开发工具 IDEA 中,会自动提示使用方法引用简化代码,你只需按 ALT+Eenter 快捷键,根据提示选择操作即可

    如果你想要深入了解方法引用的使用原则,可以继续往下看。(即使不看也没大问题,有开发工具帮你优化)

    使用方法引用改写 Comparator 例子中的 lambda 表达式

    // 
    TreeSet<Integer> ts3 = new TreeSet<>((i1, i2) -> Integer.compare(i1, i2));
    // 使用方法引用
    TreeSet<Integer> ts4 = new TreeSet<>(Integer::compare);
    
    

    (第一种情况)实现函数式接口方法的参数列表,必须和方法引用方法的参数列表保持一致

    Comparator.compare(o1, o2) 的 o1, o2 与 Integer.compare(i1, i2) 中的 i1, i2 对应,所以才能够使用方法应用。

    当函数式接口方法只有一个参数时(小例子):

       
       @Test
       public void test3() {
       	List<String> stringList = Arrays.asList("北京", "天津", "上海");
           // `Consumer.accept(t)` 的参数 t 与 `System.out.println(o)` 的 o 对应
       	show(System.out::println, stringList);
       }
       // 自定义一个函数
       void show(Consumer<String> consumer, List<String> list) {
       	for (String s : list) {
       		consumer.accept(s);
       	}
       }
    

    还有第二种情况

    TreeSet<Integer> ts3 = new TreeSet<>((i1, i2) -> i1.compareTo(i2));
    // 使用方法引用
    TreeSet<Integer> ts4 = new TreeSet<>(Integer::compareTo);
    

    Comparator.compare(o1, o2) 的 o1, o2 与 i1.compareTo(i2) 中 i1, i2 对应,这样也能使用方法引用。

    (第二种情况)假设函数式接口方法参数有 (x1, x2),而方法实现是 x1.fun(x2) 这种形式,照样使用方法引用

    如果理解了它们的规律,推而广之,可以试试抽象方法含有三个参数的情况。

    准备工作:找一个三参数的函数式接口

    	@Test
    	public void test4() {
    		String s = "Hello World";
    		Integer start = 0;
    		Integer length = 5;
    		String r1 = consume((o1, o2, o3) -> {
    			return o1.substring(o2, o3);
    		}, s, start, length);
    		System.out.println(r1);
    
    		String r2 = consume(String::substring, s, start, length);
    		System.out.println(r2);
    	}
    	String consume(TripleFunction<String, Integer, Integer, String> tripleFunction, 
                                     String s, 
                                     Integer start, 
                                     Integer length) {
    		return tripleFunction.apply(s, start, length);
    	}
    	// 自定义三参数函数式接口
    	@FunctionalInterface
    	interface TripleFunction<T, U, E, R> {
    		R apply(T t, U u, E e);
    	}
    

    这里 函数式接口 TripleFunction 的抽象方法 apply(T t, U u, E e)中的参数 t, u, e 与 s.substring(start, length) 中的 s,start, length 对应

    小结:

    设函数式接口抽象方法 abstractFun(n1, n2, n3, ..., nn)
    **有方法fun(n1, n2, n3, ..., nn)n1.fun(n2, n3, ..., nn) ** 实现了 lambda 体的代码功能
    就可使用方法引用 ClassName::fun

    构造器引用

    用法:

    
    Function<Integer, MyClass> fun1 = (i) -> new MyClass(i);
    // 使用构造器引用
    Function<Integer, MyClass> fun2 = MyClass::new;
    
    Function<Integer, Integer[]> fun3 = (n) -> new Integer[n];
    // 使用构造器引用
    Function<Integer, Integer[]> fun4 = Integer[]::new;
    

    函数式接口方法参数列表,必须和构造函数参数列表一致 (和方法应用的第一种情况相同)

  • 相关阅读:
    WSP部署错误—SharePoint管理框架中的对象“SPSolutionLanguagePack Name=0”依赖其他不存在的对象
    Elevate Permissions To Modify User Profile
    Error with Stsadm CommandObject reference not set to an instance of an object
    ASP.NET MVC3添加Controller时没有Scaffolding options
    测试使用Windows Live Writer写日志
    配置TFS 2010出现错误—SQL Server 登录的安全标识符(SID)与某个指定的域或工作组帐户冲突
    使用ADO.NET DbContext Generator出现错误—Unable to locate file
    CSS
    HTML DIV标签
    数据库
  • 原文地址:https://www.cnblogs.com/lhat/p/12186836.html
Copyright © 2011-2022 走看看