当接口里只有一个抽象方法的时候,就是函数式接口,可使用注解(@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语法:
([形参列表,不带数据类型])-> {
//执行语句
[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表达式那行会报错。