Java8 学习笔记(一)——Lambda与Functional(函数式)接口

学习是通过视频和Java8新特性终极指南

1.Lambda

Lambda常见组成形式:参数列表——>函数体

匿名内部类:

new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello World!!!");
            }
        }).start();

Lambada形式:

new Thread(()->System.out.println("Hello World!!!")).start();
  • ()包括的就是参数列表,但这里无参
  • System.out.println("Hello World!!!")函数体

1.1 参数列表

  • 没有参数时:一定要加上()
new Thread(()->System.out.println("Hello World!!!")).start();

  • 有一个参数时:
    1.如果写了参数类型,就需要加()
    2.如果没有写参数类型,可以不加()
Arrays.asList("qwe","asd","zxc").forEach((String s) -> System.out.println(s));  
Arrays.asList("qwe","asd","zxc").forEach( s -> System.out.println(s));  

  • 有两个参数时:一定需要();当参数需要加修饰符或者标签时,参数则需要加上完整的参数类型,否则,可以不加完整的参数类型。
Arrays.asList("b","a","c","d").sort((final String s1,final String s2)->s1.compareTo(s2));

1.2函数体

Lambda的函数体和普通Java的方法差不多

  • 函数体只有一行,可以省略{},需要返回值时,return也可以省略
Arrays.asList("b","a","c","d").sort((s1,s2)->s1.compareTo(s2));
  • 多行时则需要{},需要返回值时,return也不可以省略
Arrays.asList("b","a","c","d").sort((s1,s2) ->{
        int result = s1.compareTo(s2);
        return result;
});

1.3 Lambda表达式中的变量

  • 参数
Arrays.asList("b","a","c","d").sort((s1,s2)->s1.compareTo(s2));

s1,s2就是Labmbda表达式的参数


  • 局部变量和自由变量
private static void printHelloLambda(int times, String str) {
    Runnable runnable = () -> {
        for (int i = 0; i < times; i++) {
            System.out.println(str);
        }
    };
    new Thread(runnable).start();
}

运行结果就是打印timesstring
for()中的i就是局部变量

int times, String str既不是参数,也不是局部变量,而是自由变量

自由变量在Lambda表达式中不能修改。如果是采用内明内部类的形式,内部类想用使用方法的参数int times, String str,是需要加上final的。

在Lambda表达式中的this代表创建Lambda表达式方法的this,表达式所在的类。


Lambda表达式中的异常同普通方法一样,也是有两种处理方法。
1.表达式函数体内进行try-catch处理
2.接口方法进行throws。Lambda所在的方法进行throws是无效的。


1.4 Lambda表达式方法引用

Arrays.asList("b", "a", "c", "d").forEach(System.out :: println);
String[] strings = { "acb", "abc", "cb", "bc" };
List<String> list = Arrays.asList(strings);
Collections.sort(list, String::compareTo);

方法引用形式:
::静态方法
对象::方法
对象::静态方法

1.5构造方法引用

形式:类::new

Arrays.asList()返回的是ArrayList,如果我们想要可以根据需要来确定返回List的类型,就可以使用构造方法引用。

import java.util.List;
@FunctionalInterface
public interface ICreater<T extends List<?>> {
    T create();
}

定义一个接口泛型为List的子类,接口内抽象方法无参,返回值为T


public class LambdaDemo {
    public static void main(String[] args) {
        forEach();
    }

    private static void forEach() {
    
        LambdaDemo lambdaDemo = new LambdaDemo();
        
        List<String> list_2 =lambdaDemo.asList(LinkedList::new, strings);
        Collections.sort(list_2, String::compareTo);
        list.stream().forEach(System.out::println);
    }

    public <T> List<T> asList(ICreater<List<T>> creater, T... t) {
        List<T> list = creater.create();
        for (T a : t)
            list.add(a);
        return list;
    }
}

构造方法引用,接口需要有一个无参的并且一定有返回值。


2.Functional(函数式)接口

语言设计者投入了大量精力来思考如何使现有的函数友好地支持lambda。最终采取的方法是:增加函数式接口的概念。函数式接口就是一个具有一个方法的普通接口。像这样的接口,可以被隐式转换为lambda表达式。java.lang.Runnable与java.util.concurrent.Callable是函数式接口最典型的两个例子。在实际使用过程中,函数式接口是容易出错的:如有某个人在接口定义中增加了另一个方法,这时,这个接口就不再是函数式的了,并且编译过程也会失败。为了克服函数式接口的这种脆弱性并且能够明确声明接口作为函数式接口的意图,Java 8增加了一种特殊的注解@FunctionalInterface(Java 8中所有类库的已有接口都添加了@FunctionalInterface注解)。

Functional接口就是只有一个抽象方法的接口。
注意是只有一个抽象方法,不是只有一个方法。也就是说Functional接口除了一个抽象方法外,还可以有默认方法和静态方法。

2.1默认方法

默认方法就是在接口中定义一个方法用default修饰

定义一个水生生物接口IWaterAnimal

@FunctionalInterface
public interface IWaterAnimal {
     void run(String s);
     default void breathe(){
         System.out.println("可以在水中呼吸");
     }
}

一个青蛙类Forg

public class Frog implements IWaterAnimal {
    
    public static void main(String[] args) {
        Forg forg = new Forg();
        forg.breathe();
        forg.run("我是青蛙,我会跳");
    }

    @Override
    public void run(String s) {
        System.out.println(s);
    }
}

运行结果就是:
可以在水中呼吸 我是青蛙,我会跳


2.1解决接口中默认方法冲突

青蛙不仅可以在水中,还可以在陆地,再定义一个陆地动物接口ILandAnimal

@FunctionalInterface
public interface ILandAnimal {
    void run(String s);
    default void breathe(){
     System.out.println("可以在空气呼吸");
    }
}

青蛙类Frog再去实现ILandAnimal接口,这时 IWaterAnimalILandAnimal中都有breathe()方法,此时的Forg类中则必须指明breathe()是实现的哪一个接口中的方法。

public class Frog implements IWaterAnimal,ILandAnimal{
    
    public static void main(String[] args) {
        Forg frog = new Forg();
        forg.breathe();
        forg.run("我是青蛙,我会跳");
    }

    @Override
    public void run(String s) {
        System.out.println(s);
    }

    @Override
    public void breathe() {
        ILandAnimal.super.breathe();
    }
}

ILandAnimal.super.breathe();是指明了使用ILandAnimal接口中的breathe()方法,此时运行结果就变得不一样:
可以在空气呼吸 我是青蛙,我会跳
如果指明了IWaterAnimal.super.breathe();,运行结果就是:
可以在水中呼吸 我是青蛙,我会跳

当然也可以选择覆盖这个breathe()方法,在Forg类中修改方法breathe()

@Override
public void breathe() {
   System.out.println("青蛙既可以在水中呼吸也可以在空气呼吸");
}

运行结果就是:
青蛙既可以在水中呼吸也可以在空气呼吸 我是青蛙,我会跳
当实现的接口中默认方法冲突时,要通过接口名.super.方法名的方式来指定方法。



2.3父类方法与接口中默认方法相同

青蛙有个大嘴巴,写一个BigMouth的类,然后Forg继承这个BigMouth

public class BigMouth {
   public void openMouth(){
       System.out.println("张开大嘴巴");
   }
}

此时的Forg类:

public class Forg extends BigMouth implements IWaterAnimal,ILandAnimal{
    
    public static void main(String[] args) {
        Forg frog = new Forg();
        frog.breathe();
        frog.run("我是青蛙,我会跳");
        frog.openMouth();
    }

    @Override
    public void run(String s) {
        System.out.println(s);
    }

    @Override
    public void breathe() {
        System.out.println("青蛙既可以在水中呼吸也可以在空气呼吸");
    }
}

加入了第7行的方法后,运行结果
青蛙既可以在水中呼吸也可以在空气呼吸 我是青蛙,我会跳 张开大嘴巴


这时在IWaterAnimal水生动物接口中加入openMouth()方法

@FunctionalInterface
public interface IWaterAnimal {
     void run(String s);
     default void openMouth(){
         System.out.println("水生生物张开大嘴巴");
     }
     default void breathe(){
         System.out.println("可以在水中呼吸");
     }
}

运行结果
青蛙既可以在水中呼吸也可以在空气呼吸 我是青蛙,我会跳 张开大嘴巴
根据运行结果,IWaterAnimal接口中的openMouth()方法并没有运行。
当父类中的方法和接口的默认方法一样时,默认调用的父类中的方法。也就是说不要试图通过接口的默认方法来覆盖Object类的方法。
--


2.4 静态方法

@FunctionalInterface
public interface IWaterAnimal {
     void run(String s);
     static void swim(){
         System.out.println("水生生物会游泳");
     }
     default void openMouth(){
         System.out.println("水生生物张开大嘴巴");
     }
     default void breathe(){
         System.out.println("可以在水中呼吸");
     }
}

Forg类中就可以直接通过IWaterAnimal.swim()来调用。和普通的Java类的静态方法相同。

3.最后

Lambda表达式简化了代码的书写,却增加了阅读代码的难度。我现在刚刚开始学使用,回头看写的方法,往往还得思考写的是啥。现在学习写Lambda表达式的思路就是,代码中哪些可以省略,省略后JVM能不能推测出来省略的是啥,怎么省略。

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

推荐阅读更多精彩内容