Java8 函数式接口 , lambda表达式,接口默认方法

函数式接口:

当接口里只有一个抽象方法的时候,就是函数式接口,可使用注解(@FunctionalInterface)强制限定接口是函数式接口,即只能有一个抽象方法。java

例如:ide

package com.simple.lambdajava;

/**
 * Created by zhubo on 2018/4/14.
 * 默认是函数式接口
 */
public interface Interface1 {
    void method1();
}

上面的接口只有一个抽象方法,则默认是函数式接口。函数

interface Integerface2 {  
     void test();  
     void test2();  
}

该接口有两个抽象方法,不是函数式接口测试

@FunctionalInterface  
interface Integerface2 {  
      
}

上面这样写编译会报错,由于@FunctionalInterface注解声明了该接口是函数式接口,必须且只能有一个抽象方法。spa

@FunctionalInterface
public interface Interface1 {
    void method1();
}

Lambda表达式只能针对函数式接口使用。code

接口里的静态方法:

从java8开始接口里能够有静态方式,用static修饰,可是接口里的静态方法的修饰符只能是public,且默认是public。blog

package com.simple.lambdajava;

/**
 * Created by zhubo on 2018/4/14.
 * 接口里的静态方法
 */
public interface TestStaticMethod {

    static void test1() {
        System.out.println("接口里的静态方法!");
    }
}

使用接口类名调用静态方法:继承

/**
 * Created by zhubo on 2018/4/14.
 */
public class Test {
    public static void main(String[] args) {
        TestStaticMethod.test1();
    }
}

 

/**
 * Created by zhubo on 2018/4/14.
 * 接口里的静态方法
 * 也是函数式接口
 */
@FunctionalInterface
public interface TestStaticMethod {
    // 这是一个抽象方法
    void test();
    // 静态方法,不是抽象方法
    static void test1() {
        System.out.println("接口里的静态方法!");
    }
}

上面代码并不会报错,能够看到接口仍然是函数式接口接口

接口的默认方法:

java8里,除了能够在接口里写静态方法,还能够写非静态方法,可是必须用default修饰,且只能是public,默认也是public。字符串

//非静态default方法  
interface TestDefaultMethod{  
     default void test() {  
           System.out.println("这个是接口里的default方法test");  
     }  
     public default void test1() {  
           System.out.println("这个是接口里的default方法test1");  
     }  
     //编译报错  
//   private default void test2() {  
//         System.out.println("这个是接口里的default方法");  
//   }  
}

因为不是静态方法,因此必须实例化才能够调用。

public class Test {  
     public static void main(String[] args) {  
  
           //使用匿名内部类初始化实例  
           TestDefaultMethod tx = new TestDefaultMethod() {  
           };  
           tx.test();  
           tx.test1();  
     }  
}

默认方法能够被继承。可是要注意,若是继承了两个接口里面的默认方法同样的话,那么必须重写。

interface A {  
     default void test() {  
           System.out.println("接口A的默认方法");  
     }  
}  
interface B {  
     default void test() {  
           System.out.println("接口B的默认方法");  
     }  
}  
interface C extends A,B {  
  
}

这里接口c处会报错,由于编译器并不知道你到底继承的是A的默认方法还说B的默认方法。能够修改以下进行重写,用super明确调用哪一个接口的方法:

interface C extends A,B {  
  
     @Override  
     default void test() {  
           A.super.test();  
     }  
  
}

测试:

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

类继承两个有一样默认方法的接口也是同样,必须重写。

下面的代码编译会报错

class D implements A,B {  
     void test() {  
  
     }  
}

由于A或B的test方法是默认方法,修饰符为public,重写该方法修饰符必须等于或者大于它,而public已是最大的访问修饰符,因此这里修饰符必须是public

class D implements A,B {  
     @Override  
     public void test() {  
           A.super.test();  
     }  
}
public static void main(String[] args) {  
  
      D d = new D();  
      d.test();  
}

注意:默认方法并非抽象方法,因此下面这个接口仍然是函数式接口。

@FunctionalInterface  
interface A {  
     default void test() {  
           System.out.println("接口A的默认方法");  
     }  
     void test1();  
}

在接口里可使用默认方法来实现父接口的抽象方法。如:

interface C extends A,B {  
  
     @Override  
     default void test() {  
           A.super.test();  
     }  
     default void test1() {  
           System.out.println("在子接口实现父接口的抽象方法");  
     }  
  
}
C c = new C() {  
 };  
c.test1();

在实际使用匿名函数调用时能够重写:

C c = new C() {  
     @Override  
     public void test1() {  
          System.out.println("调用时重写");  
     }  
};  
c.test1();

 

能够在子接口里重写父接口的默认方法,使其成为抽象方法。

interface E {  
     default void test() {  
           System.out.println("接口E的默认方法");  
     }  
}  
interface F extends E {  
    void test();  
}

下面main方法里这样写不会报错

E e = new E(){  
  
};  
e.test();

但若是是这样:

F f = new F(){  
  
};  
f.test();

则编译报错,要求你必须实现test()方法:

能够改成:

public static void main(String[] args) {  
  
      F f = new F(){  
           @Override  
           public void test() {  
                System.out.println("F接口实现");  
           }  
      };  
      f.test();  
}

 

Lambda表达式

能够认为是一种特殊的匿名内部类

lambda只能用于函数式接口。

lambda语法:

     ([形参列表,不带数据类型])-> {

     //执行语句

     [return..;]

}

注意:

一、若是形参列表是空的,只须要保留()便可

二、若是没有返回值。只须要在{}写执行语句便可

三、若是接口的抽象方法只有一个形参,()能够省略,只须要参数的名称便可

四、若是执行语句只有一行,能够省略{},可是若是有返回值时,状况特殊。

五、若是函数式接口的方法有返回值,必须给定返回值,若是执行语句只有一句,还能够简写,即省去大括号和return以及最后的;号。

六、形参列表的数据类型会自动推断,只须要参数名称。

public class TestLambda {  
     public static void main(String[] args) {  
           TestLanmdaInterface1 t1 = new TestLanmdaInterface1() {  
                @Override  
                public void test() {  
                     System.out.println("使用匿名内部类");  
  
                }  
           };  
           //与上面的匿名内部类执行效果同样  
           //右边的类型会自动根据左边的类型进行判断  
           TestLanmdaInterface1 t2 = () -> {  
                System.out.println("使用lanbda");  
           };  
           t1.test();  
           t2.test();  
  
           //若是执行语句只有一行,能够省略大括号  
           TestLanmdaInterface1 t3 = () -> System.out.println("省略执行语句大括号,使用lanbda");  
           t3.test();  
  
           TestLanmdaInterface2 t4 = (s) -> System.out.println("使用lanbda表达式,带1个参数,参数为:"+s);  
           t4.test("字符串参数1");  
  
           TestLanmdaInterface2 t5 = s -> System.out.println("使用lanbda表达式,只带1个参数,可省略参数的圆括号,参数为:"+s);  
           t5.test("字符串参数2");  
  
           TestLanmdaInterface3 t6 = (s,i) -> System.out.println("使用lanbda表达式,带两个参数,不能够省略圆括号,参数为:"+s+"  "+ i);  
           t6.test("字符串参数3",50);  
     }  
}  
  
@FunctionalInterface  
interface TestLanmdaInterface1 {  
     //不带参数的抽象方法  
     void test();  
}  
@FunctionalInterface  
interface TestLanmdaInterface2 {  
     //带参数的抽象方法  
     void test(String str);  
}  
@FunctionalInterface  
interface TestLanmdaInterface3 {  
     //带多个参数的抽象方法  
     void test(String str,int num);  
}

 

package com.Howard.test12;  
  
public class CloseDoor {  
     public void doClose(Closeable c) {  
           System.out.println(c);  
           c.close();  
     }  
  
     public static void main(String[] args) {  
           CloseDoor cd = new CloseDoor();  
           cd.doClose(new Closeable() {  
                @Override  
                public void close() {  
                     System.out.println("使用匿名内部类实现");  
  
                }  
           });  
  
           cd.doClose( () -> System.out.println("使用lambda表达式实现"));  
     }  
}  
@FunctionalInterface  
interface Closeable {  
     void close();  
}

能够看出,lambda表达式和匿名内部类并不彻底相同

观察生成的class文件能够看出,lambda表达式并不会生成额外的.class文件,而匿名内部类会生成CloseDoor$1.class

和匿名内部类同样,若是访问局部变量,要求局部变量必须是final,若是没有加final,会自动加上。

例如:

public class TestLambdaReturn {  
     void re(LambdaReturn lr) {  
           int i = lr.test();  
           System.out.println("lambda表达式返回值是:"+i);  
     }  
  
     public static void main(String[] args) {  
           int i = 1000;  
           tlr.re( () -> i);  
             
     }  
}  
interface LambdaReturn {  
     int test();  
}  
            
若是只是上面那样写,编译不会报错,可是若是改成:  
     public static void main(String[] args) {  
           int i = 1000;  
           tlr.re( () -> i); //报错  
           i = 10;  
     }

把i看成非final变量用,则lambda表达式那行会报错。

相关文章
相关标签/搜索