Lambdas表達式是java 8的一個最重要的新特性燥狰。它允許把函數(shù)作為一個方法的參數(shù)(在一個方法中用函數(shù)來作為方法傳遞進去)科吭。或者把一段代碼看成是數(shù)據(jù)。
Lambdas表達式實例
基本語法如下:
(parameters) -> experssion
或
(parameters) -> {statements;}
舉栗說明
public class LombokTest {
public static void main(String[] args) {
// 簡單情況下不用return
Arrays.asList("a","b","c").forEach(System.out::println);
// 復雜情況下可以帶對花括號派近,像java代碼塊一樣
Arrays.asList("d","e","f").sort((e1,e2) ->{
int res = e1.compareTo(e2);
return res;
});
// runoob例子==類型聲明
MathOperation sub = (int a,int b) -> a - b;
// runoob例子==不用類型聲明
MathOperation add = (a,b) -> a+b;
LombokTest lt = new LombokTest();
System.out.println("1+2="+lt.operate(1,2,add));
System.out.println("1-2="+lt.operate(1,2,sub));
}
interface MathOperation {
int operation(int a, int b);
}
private int operate(int a, int b, MathOperation mathOperation){
return mathOperation.operation(a, b);
}
}
控制臺打印
a
b
c
1+2=3
1-2=-1
使用Lambdas表達式需要注意以下兩點
- Lambda表達式主要用來定義行內(nèi)執(zhí)行的方法類型接口备畦。例如上面的
MathOperation
接口武通。 - Lamda表達式免去了使用匿名方法的麻煩堪滨,并且給予java簡單但是強大的函數(shù)化的編程能力。
Lambdas表達式中的變量作用域
Lambdas表達式可以引用類的成員變量和局部變量蕾各。如果這些類型不是final的話扒磁,它們也會隱含的轉(zhuǎn)換成final
public class LombokTest {
int a = 12;
public static void main(String[] args) {
// 引用成員變量
LombokTest lt = new LombokTest();
Converter<Integer,String> s = (param) -> System.out.println((param + lt.a));
s.convert(8);
// 引用局部變量
String separator = ",";
Arrays.asList("a","b","c").forEach((String e) -> System.out.print(e+separator));
// 在 Lambda 表達式當中不允許聲明一個與局部變量同名的參數(shù)或者局部變量。
String first = "1";
Comparator<String> comparator = (first,second)
-> Integer.compare(first.length(),second.length()); // idea編譯報錯Variable 'first' is already defined in the scope
// Lambda中this的應用
lt.sayOk();
}
public String toString(){
return "\n are u ok?";
}
private void sayOk(){
Runnable runnable = () ->{
System.out.println(this.toString());
};
new Thread(runnable).start();
}
private interface Converter<T1,T2>{
void convert(int i);
default void say() {
}
}
}
注釋掉報錯代碼后式曲,控制臺打印
20
a,b,c,
are u ok?
NOTE
- 和匿名對象有點不同的是妨托,Lambda表達式當中引用的變量可以不用聲明為final
- Lambda表達式的字段類型為final缸榛,所以在后面的代碼中不能被修改。如例子中
String separator = ",";
后續(xù)代碼中如果修改為separator = "兰伤。";
則會被編譯器報錯 - Lambda表達式當中不允許聲明一個與局部變量重名的參數(shù)或者局部變量内颗。
- 和局部變量不同的是,Lambda內(nèi)部對于實例的字段(即:成員變量)以及靜態(tài)變量是即可讀又可寫敦腔。
- Lambda表達式中無法訪問到接口的默認方法(java 8新特性均澳,下面講到)。如上
Converter
接口的say()
是無法被訪問的符衔。 - Lambda中的this會去引用創(chuàng)建該Lambda表達式的方法的this找前,如上
are u ok?
接口的默認方法與靜態(tài)方法
接口的默認方法與靜態(tài)方法是java 8的新概念,用來擴展接口的聲明判族。與傳統(tǒng)接口不同躺盛,它可以在接口中添加方法。另外它并不受函數(shù)式接口(帶有注解@FunctionalInterface)的契約五嫂,可以任意使用颗品。
默認方法
默認方法用default
關鍵詞修飾肯尺,可以在接口中存在多個沃缘。與接口抽象方法不同,它是一個非抽象的方法實現(xiàn)则吟。它的使用方式有點類似于抽象類中的非抽象成員方法槐臀。這個特征也叫擴展方法
==Warning:==
在實現(xiàn)默認方法時,它是<font color='red'>不能夠</font>重寫Object中的方法氓仲,但是可以重載Object 中的方法水慨。例如toString
、hashCode
等方法不能被覆蓋敬扛,但是卻可以被重載調(diào)用晰洒。
interface DefaultM{
default String myDefaultMothod(){
return "https://code666.top";
}
// 因為重寫了Object方法,以下就會報錯
default String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
}
static class DefaultMM implements DefaultM{
public void test(){
System.out.println(myDefaultMothod());
}
}
public static void main(String[] args) {
DefaultMM dmm = new DefaultMM();
dmm.test();
}
靜態(tài)方法
靜態(tài)方法用static
修飾啥箭,可以通過接口名調(diào)用谍珊。
interface DefaultM2{
static DefaultM create(Supplier<DefaultM> supplier){
return supplier.get();
}
}
static class DefaultMM implements DefaultM{
public void test(){
System.out.println(myDefaultMothod());
}
}
public static void main(String[] args) {
DefaultM defaultM = DefaultM2.create(DefaultMM::new);
System.out.println(defaultM.myDefaultMothod());
}
總結(jié)
為什么接口的默認方法不能重載toString(),hashCode()和equals()?
接口不能提供對Object類的任何方法的默認實現(xiàn),因為若可以會很難確定什么時候該調(diào)用接口默認的方法急侥。并且也是毫無意義的砌滞。因為每一個java類都是Object的子類,也都繼承了它類中的equals等方法坏怪。
默認方法和靜態(tài)方法的注意點
- 接口默認方法贝润、靜態(tài)方法可以有多個。
- 默認方法通過實例調(diào)用铝宵,靜態(tài)方法通過接口名調(diào)用打掘。
- default默認方法關鍵字只能用在接口中。
- 默認方法可以被繼承,如果繼承了多個接口尊蚁,多個接口都定義了多個同樣的默認方法唯绍,實現(xiàn)類需要重寫默認方法不然會報錯。
- 靜態(tài)方法不能被繼承及覆蓋枝誊,所以只被具體所在的接口調(diào)用
函數(shù)式接口@FunctionalInterface
函數(shù)式接口(Functional Interface)就是一個有且僅有一個抽象方法况芒,但是可以有多個非抽象方法的接口。函數(shù)式接口可以被隱式轉(zhuǎn)換為 lambda 表達式叶撒。
定義如下
@FunctionalInterface
public interface Functional<T>{
void test(T t);
}
Warnning:
- 該注解只能標記在有且只有抽象方法的接口上绝骚。若接口有兩個抽象方法則編譯器會報錯(上面提到的
默認方法
和靜態(tài)方法
都不是抽象方法)。 - 因為接口也是默認繼承java.lang.Object的祠够,所以如果重寫了Object內(nèi)的方法則也不屬于抽象方法压汪。
- 并且該注解不是必須的。如果一個接口符合‘函數(shù)式接口’規(guī)范,那加不加該注解都不影響古瓤,但是加了該注解后編譯器會校驗止剖。(==也就是說如果該接口是為了函數(shù)式接口而生的,就可以加上此注解==)
實例
接口還是上面那個落君。PS初級開發(fā): static <T> void
中<T>
不是返回值穿香,表示傳入?yún)?shù)有泛型
public class FunctionalTest {
private static <T> void Test(Functional<T> fun, T t){
System.out.print("https://");
fun.test(t);
System.out.print(".top");
}
public static void main(String[] args) {
String s = "code666";
Test(System.out::print,s);
}
}
輸出 https://code666.top
通過輸入和輸出的參數(shù)來區(qū)分,函數(shù)式接口有如下四類
接口名 | 說明 | 接口方法 |
---|---|---|
Function<T,R> | 接收一個T類型的參數(shù)绎速,返回一個R類型的結(jié)果 | R apply(T t) |
Consumer<T> | 接收一個T類型的參數(shù)皮获,不返回結(jié)果 | void accept(T t) |
Predicate<T> | 接收一個T類型的參數(shù),返回一個boolean類型的結(jié)果 | boolean test(T t) |
Supplier<T> | 不接收參數(shù)纹冤,返回一個T類型的結(jié)果 | T get() |
接收兩個參數(shù)的相關函數(shù)式接口
接口名 | 說明 |
---|---|
BiFunction<T,U,R> | 接收T類型U類型兩參數(shù)洒宝,返回一個R類型結(jié)果 |
BiConsumer<T,U> | 接收T類型U類型兩參數(shù)不返回值 |
BiPredicate<T,U> | 接收T類型和U類型的兩個參數(shù),返回一個boolean類型的結(jié)果 |
總結(jié): 函數(shù)式接口
就是只包含一個抽象方法的接口
覺得還行可以點個星星哦