java8新特性之函式式介面、lambda表示式、介面的預設方法、方法和建構函式的引用
函式式介面
當介面裡只有一個抽象方法的時候,就是函式式介面,可以使用註解(@FunctionalInterface)強制限定介面是函式式介面,即只能有一個抽象方法。
例如:
public interface Integerface1 {
void test();
}
上面的介面只有一個抽象方法,則預設是函式式介面。
interface Integerface3 {
void test();
void test2();
}
該介面有兩個抽象方法,不是函式式介面
@FunctionalInterface
interface Integerface2 {
}
上面這樣寫編譯會報錯,因為@FunctionalInterface註解宣告瞭該介面是函式式介面,必須且只能有一個抽象方法。
如:
@FunctionalInterface
interface Integerface2 {
void test();
}
Lambda表示式只能針對函式式介面使用。
介面裡的靜態方法
從java8開始介面裡可以有靜態方式,用static修飾,但是介面裡的靜態方法的修飾符只能是public,且預設是public。
interface TestStaticMethod {
static void test1() {
System.out.println("介面裡的靜態方法!");
}
}
用介面類名呼叫靜態方法:
public class Test {
public static void main(String[] args) {
TestStaticMethod.test1();
}
}
//函式式介面
@FunctionalInterface
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();
}
Lanbda表示式
可以認為是一種特殊的匿名內部類
lambda只能用於函式式介面。
lambda語法:
([形參列表,不帶資料型別])-> {
//執行語句
[return…;]
}
注意:
1、如果形參列表是空的,只需要保留()即可
2、如果沒有返回值。只需要在{}寫執行語句即可
3、如果介面的抽象方法只有一個形參,()可以省略,只需要引數的名稱即可
4、如果執行語句只有一行,可以省略{},但是如果有返回值時,情況特殊。
5、如果函式式介面的方法有返回值,必須給定返回值,如果執行語句只有一句,還可以簡寫,即省去大括號和return以及最後的;號。
6、形參列表的資料型別會自動推斷,只需要引數名稱。
package com.Howard.test12;
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表示式那行會報錯。
方法的引用
引用例項方法:自動把呼叫方法的時候的引數,全部傳給引用的方法
<函式式介面> <變數名> = <例項> :: <例項方法名>
//自動把實參傳遞給引用的例項方法
<變數名>.<介面方法>([實參])
引用類方法:自動把呼叫方法的時候的引數,全部傳給引用的方法
引用類的例項方法:定義、呼叫介面方法的時候需要多一個引數,並且引數的型別必須和引用例項方法的型別必須一致,
把第一個引數作為引用的例項,後面的每個引數全部傳遞給引用的方法。
interface <函式式介面> {
<返回值> <方法名>(<類名><名稱> [,其它引數…])
}
<變數名>.<方法名>(<類名的例項>[,其它引數])
具體例子參考下面程式碼
構造器的引用
把方法的所有引數傳遞給引用的構造器,根據引數的型別來推斷呼叫的構造器。
參考下面程式碼
package com.Howard.test12;
import java.io.PrintStream;
import java.util.Arrays;
/**
* 測試方法的引用
* @author Howard
* 2017年4月14日
*/
public class TestMethodRef {
public static void main(String[] args) {
MethodRef r1 = (s) -> System.out.println(s);
r1.test("普通方式");
//使用方法的引用:例項方法的引用
//System.out是一個例項 out是PrintStream 型別,有println方法
MethodRef r2 = System.out::println;
r2.test("方法引用");
//MethodRef1 r3 =(a)-> Arrays.sort(a);
//引用類方法
MethodRef1 r3 = Arrays::sort;
int[] a = new int[]{4,12,23,1,3};
r3.test(a);
//將排序後的陣列輸出
r1.test(Arrays.toString(a));
//引用類的例項方法
MethodRef2 r4 = PrintStream::println;
//第二個之後的引數作為引用方法的引數
r4.test(System.out, "第二個引數");
//引用構造器
MethodRef3 r5 = String::new;
String test = r5.test(new char[]{'測','試','構','造','器','引','用'});
System.out.println(test);
//普通情況
MethodRef3 r6 = (c) -> {
return new String(c);
};
String test2 = r6.test(new char[]{'測','試','構','造','器','引','用'});
System.out.println(test2);
}
}
interface MethodRef {
void test(String s);
}
interface MethodRef1 {
void test(int[] arr);
}
interface MethodRef2 {
void test(PrintStream out,String str);
}
//測試構造器引用
interface MethodRef3 {
String test(char[] chars);
}
相關文章
- ?Java8新特性之Lambda表示式,函式式介面,方法引用和default關鍵字Java函式
- java8的新特性之lambda表示式和方法引用Java
- JDK1.8新特性:Lambda表示式語法和內建函式式介面JDK函式
- java8 新特性之函式式介面Java函式
- Java8新特性探索之函式式介面Java函式
- Java8的新特性--函式式介面Java函式
- java8 新特性之Lambda 表示式Java
- java8特性-函式式介面Java函式
- JDK1.8之內建函式式介面(方法引用的實現)JDK函式
- kotlin 函式和 Lambda 表示式Kotlin函式
- 預設建構函式、引數化建構函式、複製建構函式、解構函式函式
- 5.函式和lambda表示式函式
- Python函式與lambda 表示式(匿名函式)Python函式
- Lambda表示式入門--函數語言程式設計與函式式介面函數程式設計函式
- Java8新特性(一)-Lambda表示式Java
- Java8新特性(1):Lambda表示式Java
- Java8新特性-四大核心函式式介面Java函式
- JDK8新特性之函式式介面JDK函式
- 【Python】python map()函式和lambda表示式Python函式
- java8特性-lambda表示式Java
- 預設建構函式和帶預設值的建構函式不能同時存在函式
- Java8之Stream-函式式介面Java函式
- jdk1.8-Lambda函式表示式JDK函式
- 課時21:函式:lambda表示式函式
- jdk1.8Lambda函式表示式JDK函式
- 初識Lambda表示式(匿名函式)函式
- 【碼上開學】Kotlin 的高階函式、匿名函式和 Lambda 表示式Kotlin函式
- 好程式設計師分享java8新特性之Lambda表示式程式設計師Java
- Python 擴充之特殊函式(lambda 函式,map 函式,filter 函式,reduce 函式)Python函式Filter
- JDK 1.8 新特性之Lambda表示式JDK
- java-反射,介面新特性,Lambda表示式Java反射
- C#10新特性-lambda 表示式和方法組的改進C#
- 建構函式方式建立正規表示式函式
- 「Java8系列」神奇的函式式介面Java函式
- Java8新特性——從Lambda表示式到Stream流Java
- 簡析JAVA8函式式介面Java函式
- JDK1.8新特性之Lambda表示式JDK
- JDK1.8新特性之Lambda表示式()->JDK