Java8实战之-Lambda

应该在什么地方使用Lambda表达式

你可以在函数式的接口上使用Lambda表达式。
什么是函数式接口?
接口只定义了一个抽象方法,那么该接口就是函数式接口(Java8中接口还可以拥有默认方法,哪怕有多个默认方法,但是只要只定义了一个抽象方法,那么它就是一个函数式接口)。

Lambda表达式的构成

Lambda表达式分三个部分组成,Lambda表达式允许直接以内联的形式为函数式接口的抽象方法提供实现,并且把整个表达式作为函数式接口的实例,表达式构成如下:

  • 参数列表
  • 箭头(->)
  • Lambda主体(类似匿名函数的实现)

如:

 //函数式接口
 public interface Predicate<T> {
    boolean test(T t);
 }
 //Lambda例子
 Predicate<Apple> s = (Apple a) -> a.getWeight() > 100;
 Apple a = new Apple("red", 150);
 System.out.println(s.test(a));
 //实体
 public class Apple {
    private String coler;
    private int weight;
    //省略构造函数和getter、setter方法
 }

值得注意的是(Lambda表达式必须和函数式接口的唯一抽象方法的签名一致):

  • Lambda表达式的参数列表必须和对应的函数式接口的抽象方法的参数列表一致
  • Lambda表达式的主体的返回值必须和对应的函数式接口的抽象方法的返回值一致

@FunctionInterface注解

此注解用于表示该接口会被设计为一个函数式接口,如果使用此注解但是接口中超过一个抽象方法,编译器将会返回一个提示原因的错误。

Java8一些内建的函数式接口

  1. java.util.function.Predicate<T>
    只有一个 boolean test<T t>方法,接收参数类型T,返回布尔值。用法如下:
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
        List<T> results = new ArrayList<>();
        for (T s : list) {
            if (p.test(s)) {
                results.add(s);
            }
        }
        return results;
    }

    public static void main(String[] args) {
        List<String> listOfStrings = new ArrayList<>();
        listOfStrings.add("ssss");
        listOfStrings.add("zjc");
        listOfStrings.add("");
        //Predicate的lambda主体用于判断字符串是否为空
        List<String> noEmpty = filter(listOfStrings, s -> !s.isEmpty());
        System.out.println(noEmpty.toString());
    }

2.java.util.function.Consumer<T>
它接受泛型T的对象,没有返回(void)。

public static <T> void forEach(List<T> list, Consumer<T> c){
     for(T i: list){
       c.accept(i);
     }
}
forEach(Arrays.asList(1,2,3,4,5),
(Integer i) -> System.out.println(i));

3.java.util.function.Function<T, R>
定义了一个叫作apply的方法,它接受一个泛型T的对象,并返回一个泛型R的对象

public static <T, R> List<R> map(List<T> list,Function<T, R> f) {
List<R> result = new ArrayList<>();
for(T s: list){
result.add(f.apply(s));
}
return result;
}
// [7, 2, 6]
List<Integer> l = map(Arrays.asList("lambdas","in","action"),(String s) -> s.length());

4.另外还有一些原始类型特化DoublePredicate、IntConsumer、LongBinaryOperator、IntFunction

Lambda的类型检查、类型推断和限制

类型检查
Lambda的类型是从使用Lambda的上下文推断出来的,上下文(如接受它传递的方法的参数或者接受它的值的局部变量)中Lambda的表达式需要的类型称为目标类型。
类型检查的过程大致分解:

  • 首先,你要找出filter方法的声明。
  • 第二,要求它是Predicate<Apple>(目标类型)对象的第二个正式参数。
  • 第三,Predicate<Apple>是一个函数式接口,定义了一个叫作test的抽象方法。
  • 第四,test方法描述了一个函数描述符,它可以接受一个Apple,并返回一个boolean。
  • 最后,filter的任何实际参数都必须匹配这个要求。
    如果Lambda表达式抛出一个异常,那么抽象方法所声明的throws语句必须与之匹配。
    如果一个Lambda的主体是一个语句表达式, 它就和一个返回void的函数描述符兼容(当
    然需要参数列表也兼容。

如下图:


lambda_type_verification.png

类型推断
Java编译器会从上下文(目标类型)推断出用什么函数式接口来配合Lambda表达式,这意味着它也可以推断出适合Lambda的签名,因为函数描述符可以通过目标类型来得到。
如下图:

lambda_type_conclude.png

对局部变量的限制
局部变量必须显式声明为final或事实上是final

方法引用

格式:目标引用::方法的名称
方法引用的三种类型:

  • 指向静态方法的方法引用(例如Integer的parseInt方法,写作Integer::parseInt)
  • 指向任意类型实例方法的方法引用( 例如String 的length 方法, 写作String::length)
  • 指向现有对象的实例方法的方法引用(假设你有一个局部变量expensiveTransaction用于存放Transaction类型的对象,它支持实例方法getValue,那么你就可以写expensiveTransaction::getValue)
    </br>
    例子:
Lambda 等效的方法引用
(Apple a)-> a.getWeight() Apple::getWeight
( ) -> Thread.currentThread( ).dumpStack( ) Thread.currentThread( )::dumpStack( )
(str,i)-> str.substring(i) String::substring

</br>例子图示:

lambda_method_refrence.png

Lambda和方法引用最佳实践

1.传递代码

    public static void main(String[] args){
        List<Apple> inventory = new ArrayList<>();
        //...inventory.add(new Apple(...))
        inventory.sort(new AppleComparator());
    }
    public static class AppleComparator implements Comparator<Apple>{

        @Override
        public int compare(Apple o1, Apple o2) {
            return o1.getWeight().compareTo(o2.getWeight());
        }
    }

2.使用匿名类

        inventory.sort(new Comparator<Apple>() {
            @Override
            public int compare(Apple o1, Apple o2) {
                return o1.getWeight().compareTo(o2.getWeight());
            }
        });

3.使用Lambda表达式

inventory.sort((o1, o2) -> o1.getWeight().compareTo(o2.getWeight()));
//或者使用比较器的静态方法进一步简化:
inventory.sort(Comparator.comparing((a) -> a.getWeight()));

4.使用方法引用

inventory.sort(Comparator.comparing(Apple::getWeight));

复合Lambda表达式

以下使用的都是接口的默认方法或者说是非抽象方法

1.比较器复合

lambda_comparators.png

2.谓词复合
谓词接口包括三个方法:negate、and和or,让你可以重用已有的Predicate来创建更复杂的谓词。

lambda_logic.png

3.函数复合
把Function接口所代表的Lambda表达式复合起来。Function接口为此配了andThen和compose两个默认方法,它们都会返回Function的一个实例。

lambda_function.png

参考资料: 《Java 8 in action》

End on 2017-5-13 11:16.
Help yourselves!
我是throwable,在广州奋斗,白天上班,晚上和双休不定时加班,晚上有空坚持写下博客。
希望我的文章能够给你带来收获,共勉。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 196,099评论 5 462
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 82,473评论 2 373
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 143,229评论 0 325
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,570评论 1 267
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,427评论 5 358
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,335评论 1 273
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,737评论 3 386
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,392评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,693评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,730评论 2 312
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,512评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,349评论 3 314
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,750评论 3 299
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,017评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,290评论 1 251
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,706评论 2 342
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,904评论 2 335

推荐阅读更多精彩内容