函数式接口和lambda表达式

函数式接口和lambda表达式

函数式接口(Functional Interface) :

​ 任何接口,如果只包含唯一 一个抽象方法,那么它就是一个FI。(之前它们被称为 SAM类型,即 单抽象方法类型(Single Abstract Method))。接口中的方法默认就是public abstract的。

​ 接口可能继承了一个 Object 已经提供的方法,比如 toString()equals( )…这些都不属于函数式接口方法的范畴, 所以函数式接口中所说的方法不包括这些。例如下面FI接口也是一个函数式接口。

@FunctionalInterface
Interface FI{
   judge(int a);
   equals();      
}

​ API作者们可以通过 @FunctionalInterface 注解来显式指定一个接口是函数式接口。加上这个注解,接口中函数式接口方法少于一个或者多余一个,编译器都会提示错误。

lambda表达式

lambda表达式是匿名方法 ,它提供了一个正常的参数列表和一个使用这些参数的主体(body,可以是一个表达式或一个代码块)。

// 1. 不需要参数,返回值为 5
() -> 5
// 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x
// 3. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y
// 4. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> { System.out.print(s) }

lambda 表达式的语法由参数列表、箭头符号 -> 和函数体组成。函数体既可以是一个表达式,也可以是一个语句块。

假设我们想对一个List按字符串长度进行排序,那么在Java8之前,可以借助匿名内部类来实现:

List<String> words = Arrays.asList("apple", "banana", "pear");
words.sort(new Comparator<String>() {

    @Override
    public int compare(String w1, String w2) {
        return Integer.compare(w1.length(), w2.length());
    }

});

上面的匿名内部类简直可以用丑陋来形容,唯一的一行逻辑被五行垃圾代码淹没。根据前面的定义(并查看Java源代码)可知,Comparator是个FI,所以,可以用Lambda表达式来实现:

words.sort((String w1, String w2) -> {
    return Integer.compare(w1.length(), w2.length());
});

代码变短了好多!仔细观察就会发现,Lambda表达式,很像一个匿名的方法,只是圆括号内的参数列表和花括号内的代码被->分隔开了。垃圾代码写的越少,我们就有越多的时间去写真正的逻辑代码,不是吗?是的!圆括号里的参数类型是可以省略的:

words.sort((w1, w2) -> {
    return Integer.compare(w1.length(), w2.length());
});

如果Lambda表达式的代码块只是return后面跟一个表达式,那么还可以进一步简化:

words.sort(
    (w1, w2) -> Integer.compare(w1.length(), w2.length())
);

注意,表达式后面是没有分号的!如果只有一个参数,那么包围参数的圆括号可以省略:

words.forEach(word -> {
    System.out.println(word);
});

如果表达式不需要参数呢?好吧,那也必须有圆括号,例如:

Executors.newSingleThreadExecutor().execute(
    () -> {/* do something. */} // Runnable
);

怎么用Lambda表达式

​ 既然Lambda表达式这么好用,那么,可以在哪些地方使用呢?如果你真正明白了什么是FI(很容易),应该立刻就能给出答案:任何可以接受一个FI实例的地方,都可以用Lambda表达式。比如,虽然上面给出的例子都是把Lambda表达式当作方法参数传递,可以接受FI作为参数的地方,都可以替换为Lambda表达式。

但实际上你也可以定义变量:

Runnable task = () -> {
    // do something
};

Comparator<String> cmp = (s1, s2) -> {
    return Integer.compare(s1.length(), s2.length());
};

看两个例子:

使用已有的FI

JDK 1.8 新增加的函数接口:java.util.function 中Predicate 接口是一个函数式接口,它接受一个输入参数 T,返回一个布尔值结果。

eval( )方法的第二个参数是FI, 所以在这里可以用Lambda表达式.

import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

public class Java8Tester {
   public static void main(String args[]){
      List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
      System.out.println("输出所有数据:");

      // Predicate<Integer> predicate = n -> true
      // n 是一个参数传递到 Predicate 接口的 test 方法
      // n 如果存在则 test 方法返回 true

      // 传递参数 n
      eval(list, n->true);

      // Predicate<Integer> predicate1 = n -> n%2 == 0
      // n 是一个参数传递到 Predicate 接口的 test 方法
      // 如果 n%2 为 0 test 方法返回 true

      System.out.println("输出所有偶数:");
      eval(list, n-> n%2 == 0 );

      // Predicate<Integer> predicate2 = n -> n > 3
      // n 是一个参数传递到 Predicate 接口的 test 方法
      // 如果 n 大于 3 test 方法返回 true

      System.out.println("输出大于 3 的所有数字:");
      eval(list, n-> n > 3 );
   }

   public static void eval(List<Integer> list, Predicate<Integer> predicate) {
      for(Integer n: list) {

         if(predicate.test(n)) {
            System.out.print(n + " ");
         }
      }
   }
}

输出

输出所有数据:
1 2 3 4 5 6 7 8 9 输出所有偶数:
2 4 6 8 输出大于 3 的所有数字:
4 5 6 7 8 9 

自定义FI

自定义一个FI,里面有一个add( ) 方法。 想不出什么好的例子,随便举个例子很简单能说明问题足矣

package com.java8;
/**
 * Created by susq on 2017-6-20.
 */
@FunctionalInterface
public interface Fitest {
    int add(int a, int b);
}
package com.java8;
/**
 * Created by susq on 2017-6-19.
 */
public class LamJava8 {
    public static void main(String[] args) {
        int a = 2, b = 3;
        int result = addMethod( a, b, (c, d) -> { return c+d; } );
        System.out.println( result );
    }

    public static int addMethod(int a, int b, Fitest fi) {
        return fi.add(a, b);
    }
}

输出:5

方法addMethod(int a, int b, Fitest fi) 第三个参数是我们自定义的FI, 因此这里可以用(c, d) -> { return c+d; }表达式,c, d是Fitest 中 add( ) 方法的两个参数, { return c+d; } 是add 方法的方法体。这里c, d不能写a, b,会提示变量重复定义编译错误。 因为(c, d) 实质为像(int c, int d) 一样的形参, int被我们省略了。

参考:http://www.tuicool.com/articles/beA7V3
http://lucida.me/blog/java-8-lambdas-insideout-language-features/

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值