Java8 Interface Lambda

来源:互联网 发布:服装软件有哪些 编辑:程序博客网 时间:2024/06/03 19:38

Java是一门面向对象编程语言。面向对象编程语言和函数式编程语言中的基本元素(Basic Values)都可以动态封装程序行为:面向对象编程语言使用带有方法的对象封装行为,函数式编程语言使用函数封装行为。但这个相同点并不明显,因为Java的对象往往比较“重量级”:实例化一个类型往往会涉及不同的类,并需要初始化类里的字段和方法。

在Java8中接口也很多变化:

1、接口变量的访问

1) 局部变量:

  • 从Java8开始,如果在内部类里面访问局部变量,会自动给局部变量加上final修饰
  • Java8之前,内部类只能访问final的局部变量

2) 类变量:
内部类可以直接访问外部类变量,相当于是当前类访问一样。

3)实例变量:

  • 如果在静态的方法里面写上匿名内部类,访问实例变量,必须通过外部类的实例来进行访问
  • 如果外部类的实例是一个局部变量,该实例对应的变量不能多次赋值。但实例变量本身不会有影响

2、函数式接口 – Lambda的使用

函数式接口:当接口只有一个抽象方法的时候(可以有其它默认方法),就是函数式接口,可以使用注解(@FunctionalInterface)强制限定接口只能有一个抽象方法。

lambda语法:

([形参列表,不带数据类型]) -> {    // 执行语句    [return ...;]}

其中:

() : 表示参数列表,不需要指定参数类型,会自动推断
-> : 连接符
{} : 表示方法体

注意点:

  • 1.如果形参列表是空的,只需要保留()即可。
  • 2.如果没有返回值,只需要在{}写执行语句即可。
  • 3.如果接口的抽象方法只有一个形参,()可以省略,只需要参数的名称即可
  • 4.如果执行语句只有一行,可以省略{},但是如果有返回值的时候,不能省略
  • 5.形参列表的数据类型自动推断,只要参数名称
  • 6.如果函数式接口的方法有返回值,必须要给定返回值,如果执行语句只有一行代码,可以省略大特号,但必须同时省略return关键字

lambda表达式就是函数式接口,也可以认为是一种特殊的匿名内部类。下面就看看匿名内部类的lamdba表达式的写法:

1) 方法无参数,无返回值

public class LambdaDemo {    public static void main(String[] args) {        // 1. 匿名内部类的方式实现,在Java8之前,没有Lambda表达式        UserService userService = new UserService() {            @Override            public void test() {                System.out.println("不使用lambda表达式");            }        };        userService.test();        // lambda 右边的类型,会自动根据左边的变量的类型进行推断        UserService userService1 = () -> {            System.out.println("使用lambda表达式");        };        userService1.test();        // lambda 如果方法体只有一句话,可以省略大括号以及省略一个分号        // 如果有返回值,连return也可以省略        UserService userService2 = () -> System.out.println("使用最简lambda表达式");        userService2.test();     }}@FunctionalInterface// 没有参数,没有返回值interface UserService{    void test();}

Result:

这里写图片描述

2) 有一个参数,无返回值

public class LambdaDemo {    public static void main(String[] args) {        // 2 方法有一个参数,园括号里面只需要知道参数的名称,不需要参数的类型。        // 数据类型自动根据函数式接口的定义自动推断        UserService1 test1 = (x) -> {            System.out.println("一个参数,一行代码输出参数的值 : " + x);        };        test1.test(100);        // 如果参数列表里面,只有一个参数,可以省略园括号        UserService1 test2 = x -> System.out.println("一个参数,一行代码输出参数的值 : " + x);        test2.test(100);    }}@FunctionalInterface// 有一个参数,没有返回值interface UserService1{    void test(int i);}

3) 有二个参数,没有返回值

public class LambdaDemo {    public static void main(String[] args) {        UserService2 test3 = (x, y) -> {        System.out.println("两个参数 : " + x);        System.out.println("两个参数 : " + y);        };        test3.test(100, 200);    }}@FunctionalInterface// 有二个参数,没有返回值interface UserService2{    void test(int i, int j);}

4) 有一个参数,有返回值

public class LambdaDemo {    public static void main(String[] args) {            // 4 有返回值        UserService3 test4 = b -> {            b = b + 10;            return b;        };        int o = test4.test(15);        System.out.println(o);        // 如果省略大括号,return一定要省略掉。代码里面的表达式返回值会自动作为方法的返回值        UserService3 test5 = b -> b + 10;        System.out.println(test5.test(15));    }}@FunctionalInterface// 有一个参数,有返回值interface UserService3{    int test(int i);}

3、方法的引用 – Lambda的使用

1) 引用实例方法

public class TestMethodRef {    public static void main(String[] args) {        MethodRef r = s -> System.out.println(s);        r.test("字符串的");        // 使用方法的引用 : 实例方法的引用        // System.out是一个实例        MethodRef r1 = System.out :: println;        r1.test("方法引用");    }}@FunctionalInterfaceinterface MethodRef{    void test(String s);}

2) 引用类方法

public class TestMethodRef {    public static void main(String[] args) {            // 能够根据函数式接口的方法参数,推断引用的方法的参数的数据类型        // 不引用方法进行排序        MethodRef1 r3 = (o) -> Arrays.sort(o);        // 引用类方法        MethodRef1 r2 = Arrays :: sort;        int[] a = new int[]{4, 12, 32, 44, 5, 9};        // 引用方法排序        r2.test(a);        // 引用方法输出        r1.test(Arrays.toString(a));    }}@FunctionalInterfaceinterface MethodRef1{    void test(int[] arr);}

3) 引用类实例方法

public class TestMethodRef {    public static void main(String[] args) {        // *** 引用类的实例方法        MethodRef2 r4 = PrintStream :: println;        // 第二个之后的参数,作为引用方法的参数        r4.test(System.out, "第二个参数");    }}@FunctionalInterfaceinterface MethodRef2{    void test(PrintStream out, String str);}

4) 引用构造器

public class TestMethodRef {    public static void main(String[] args) {        // 引用构造器,根据函数式接口的方法名来推断引用哪个构造器        MethodRef4 r5 = String :: new;        String ok = r5.test(new char[]{'阿' , '器'});        System.out.println(ok);        MethodRef4 r6 = (c) -> {return new String(c);};        String o1 = r6.test(new char[]{'阿' , '器'});        System.out.println(o1);    }}// 测试构造器引用@FunctionalInterfaceinterface MethodRef4{    String test(char[] str);}

4、接口中的静态方法

从java8开始接口里面可以有静态方法(之前接口中是不能定义静态方法的),和普通类里面的静态方法类似,使用static修饰,但是接口里面的只能是public的。格式为:

[public] static <返回值> <方法名> ([形参列表]){    // 方法体}

例子如下:

public interface TestStaticMethod {    // 这是一个函数式接口,因为这个接口里面只有一个抽象方法    public void test();    // 静态方法不是抽象方法    static void test1(){        System.out.println("这个是接口里面的静态方法,直接可以使用接口调用此方法");    }    public static void main(String[] args) {        System.out.println("自从接口可以有静态方法,从此接口可以写main方法,可以作为程序的入口");        TestStaticMethod.test1();    }}class TestStaticMethodClass{    public static void main(String[] args) {        // 调用接口的静态方法        TestStaticMethod.test1();    }}

5、接口中的默认方法

在Java8中除了可以在接口里面写静态方法,还可以写非静态方法,但是必须用default进行修饰。

public interface TestDefaultMethod {    // 使用 default 修改的方法,表示实例方法, 必须通过实例来方法    public default void test(){        System.out.println("这个是接口里面的默认方法" + this);    }    public static void main(String[] args) {        // 使用匿名内部类初始化实例        TestDefaultMethod tdm = new TestDefaultMethod() {};        // 使用对象来访问默认方法        tdm.test();    }}

Result:

这里写图片描述

接口中可以使用this关键字,但是我们可以看到结果中有$,这是不是和我们的匿名内部类有点像了?

注意:

  • 默认方法可以被继承。如果继承多个父接口有重复的默认方法被继承到子接口,必须使用super引用明确指定调用哪个接口的默认方法在子接口必须重写重复方法,并使用下面的语法重写父接口方法重复的问题
<父接口类名>.super.<重复的方法名>([参数]);
  • 同样,如果实现了多个接口,遇到有重复的默认方法,也需要使用重写重复的方法,使用super引用解决问题,和接口一样。
  • 父接口的抽象方法,在子接口里面可以使用默认方法实现,这样是实现类里面就不需要再实现了。如果实现类再去实现默认方法,那么相当于是”方法覆盖”。
  • 如果父接口有一个抽象方法,在子接口里面可以重写为抽象方法(去掉父接口的形为)

1) 默认方法多继承

interface A{    default void test(){        System.out.println("接口A里面的默认方法");    }}interface B{    default void test(){        System.out.println("接口B里面的默认方法");    }}public interface C extends A, B {    // 明确指定引用父接口,使用super引用调用父接口的默认方法    // <父接口类名>.super.<重复的方法名>([参数])    default void test(){        B.super.test();        System.out.println("接口C重写的默认test方法");    }}

如果注释掉C接口中的test方法就会报以下的错误:

这里写图片描述

Test

class Test{    public static void main(String[] args) {        C c = new C(){};        c.test();    }}

Result:

这里写图片描述

2) 默认方法的重写

public interface E {    default void test(){        System.out.println("默认方法");    }}interface F extends E{    // 在子接口重写父接口的默认方法,把默认方法改为抽象方法    void test();}class RemoveDefaultMethod {    public static void main(String[] args) {        E e = new E(){};        e.test();        F f = () -> System.out.println("匿名内部类重写父接口方法");        f.test();    }}

Result:

这里写图片描述

推荐阅读: 深入理解Java 8 Lambda

2 0
原创粉丝点击