函數式編程
從JDK1.8開始為了簡化使用者進行代碼的開發,專門提供有lambda表達式的支持,利用此操作形式可以實現函數式的編程,對於函數編程比較著名的語言是:haskell、Scala,利用函數式的編程可以避免掉面向對象編程過程中的一些繁瑣的問題。
面向對象在其長期發展的過程中一直有一部分的反對者,這些反對者認為面向對象的設計過於復雜繁瑣,以一個最簡單的程序為例:
范例:觀察傳統開發中的問題
1 interface IMessage{ 2 public void send(String str); 3 } 4 public class Main { 5 6 public static void main(String[] args) { 7 IMessage msg=new IMessage() { 8 @Override 9 public void send(String str) { 10 System.out.println("消息發送:"+str); 11 } 12 }; 13 msg.send("hello"); 14 } 15 }
1,Lamda表達式在這個程序中,實際上核心的功能只有一行語句【System.out.println("消息發送:"+str);】,但是為了這樣一行核心語句我們需要按照完整的面向對象給出的設計結構進行開發。於是這些問題隨着技術的不斷發展也是越來越突出。
范例:使用Lambda表達式實現上面相同的功能
1 interface IMessage{ 2 public void send(String str); 3 } 4 public class Main { 5 6 public static void main(String[] args) { 7 IMessage msg=(str)->{ 8 System.out.println("消息發送:"+str);}; 9 msg.send("hello"); 10 } 11 }
Lambda表達式如果想要使用,那么必須有一個重要的實現要求:SAM(Single Abstract Method),只有一個抽象方法,在這個接口里面只是提供一個send()方法,除此之外沒有任何其他方法定義,所以這樣的接口就被定義為函數式接口,而只有函數式接口才能被Lambda表達式所使用。現在整個程序代碼里面會發現真的只是編寫了一行語句,於是利用這種形式就避免了復雜的面向對象結構化的要求。
范例:函數式接口里面的方法只能有一個
1 @FunctionalInterface//函數式接口 2 interface IMessage{//JDK1.8之后接口里面可以定義普通方法和Static方法 3 public void send(String str); 4 public default void print() {//這是一個 5 System.out.println("公共方法"); 6 } 7 } 8 public class Main { 9 10 public static void main(String[] args) { 11 IMessage msg=(str)->{ 12 System.out.println("消息發送:"+str);}; 13 msg.send("hello"); 14 msg.print(); 15 } 16 }
①方法沒有參數:()->{}對於Lambda表達式而言,需要提供有如下幾種格式:
②方法有參數:(參數,參數)->{}
③如果現在只有一行語句返回:(參數,參數)->語句;
范例:定義沒有參數的方法
1 @FunctionalInterface//函數式接口 2 interface IMessage{//JDK1.8之后接口里面可以定義普通方法和Static方法 3 public void send(); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IMessage msg=()->{ 8 System.out.println("消息發送:這是個無參lambda形式");}; 9 msg.send(); 10 } 11 }
范例:定義有參數的
1 @FunctionalInterface//函數式接口 2 interface IMath{//JDK1.8之后接口里面可以定義普通方法和Static方法 3 public int add(int x,int y); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IMath math=(t1,t2)->{ 8 return t1+t2; 9 }; 10 System.out.println(math.add(10,20)); 11 } 12 }
范例:簡化Lambda操作以上表達式只有一行語句,這個時候也可以進一步簡化。
1 @FunctionalInterface//函數式接口 2 interface IMath{//JDK1.8之后接口里面可以定義普通方法和Static方法 3 public int add(int x,int y); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IMath math=(t1,t2)->t1+t2;{ 8 }; 9 System.out.println(math.add(10,20)); 10 } 11 }
利用lambda可以擺脫傳統面向對象的關於結構的限制,使得代碼更加的簡便。只用於替換SAM的函數式接口
2,方法引用
引用數據類型最大的特點是可以進行內存的指向處理,但是在我們傳統的開發之中一直所使用的只是對象的引用操作,從JDK1.8之后也有方法的引用,即不同的方法名稱可以描述同一個方法。如果進行方法的引用在java里面提供有如下的四種形式。
①引用靜態方法: 類名稱::static 方法名稱;
②引用某個實例對象的方法: 實例化對象::普通方法;
③引用特定類型的方法: 特定類::普通方法;
④引用構造方法: 類名稱::new。
在String類里面提供有String.valueOf()方法,這個方法屬於靜態方法。
方法定義:public static String valueOf(int i),該方法有參數,並且有返回值;
·范例:
1 @FunctionalInterface//函數式接口 2 interface IFunction<P,R>{//P描述參數,R描述返回值 3 public R change(P p); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IFunction<Integer,String>fun=String::valueOf; 8 String str=fun.change(100); 9 System.out.println(str.length()); 10 } 11 }
·范例:引用實例化對象中的方法利用方法引用這一概念可以為一個方法定義多個名字,但是要求必須是函數式接口。
在String類里面有一個轉大寫的方法:public String toUpperCase();
·這個方法是必須有實例化對象提供的情況下才可以調用;
1 @FunctionalInterface//函數式接口 2 interface IFunction<R>{//P描述參數,R描述返回值 3 public R upper(); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IFunction<String>fun="hello"::toUpperCase; 8 String str=fun.upper(); 9 System.out.println(str); 10 } 11 }
這是一個普通方法,如果要引用普通方法,則往往都需要實例化對象,但是如果說現在你不想給出實例化對象,只是想引用這個方法。則就可以使用特定類進行引用處理。在進行方法引用的時候也可以進行特定類的一些操作方法,在String類中提供一個字符串大寫關系的比較: public int compareTo(String anotherString);
·范例:引用特定類中的方法
1 @FunctionalInterface//函數式接口 2 interface IFunction<P>{//P描述參數,R描述返回值 3 public int compare(P p1,P p2); 4 } 5 public class Main { 6 public static void main(String[] args) { 7 IFunction<String>fun=String::compareTo; 8 System.out.println(fun.compare("A","a")); 9 } 10 }
范例:引用構造方法在方法里面最具殺傷力的是構造方法的引用。
1 class Person{ 2 private String name; 3 private int age; 4 public Person(String name,int age){ 5 this.name=name; 6 this.age=age; 7 } 8 @Override 9 public String toString() { 10 return "姓名:"+this.name+",年齡:"+this.age; 11 } 12 } 13 @FunctionalInterface//函數式接口 14 interface IFunction<R>{//P描述參數,R描述返回值 15 public R create(String s,int a); 16 } 17 public class Main { 18 public static void main(String[] args) { 19 IFunction<Person>fun=Person::new; 20 System.out.println(fun.create("Wanyu",25)); 21 } 22 }
提供方法引用的概念更多的情況下也只是彌補了對於引用的支持功能。
3,內建函數式接口
在JDK1.8之中提供Lambda表達式也提供方法引用,但是你會發現現在由開發者自己定義函數式接口往往需要使用【@FunctionInterface】注解來進行大量的聲明,於是很多的情況下如果為了方便則可以引用系統中提供的函數式接口。
在系統值周專門提供有有一個java.util.function的開發包,里面可以直接使用函數式接口,在這個包下面可以直接使用如下的幾個核心接口:
①功能性函數式接口:
·在String類中有一個方法判斷是否以指定的字符串開頭:public boolean startsWith(String str)
接口定義 |
接口使用 |
@FunctionalInterface public interface Function<T,R>{ public R apply(T t); } |
import java.util.function.*; |
②消費型函數式接口:只能進行數據處理操作
·在進行系統數據輸出的時候使用的是:System.out.println()
接口定義 |
接口使用 |
@FunctionalInterface public interface Consumer<T>{ public void accept(T t); } |
import java.util.function.*; |
③供給型函數式接口:
·在String類中提供有轉小寫方法,這個方法沒有接收參數,但是有返回值;
|-方法:public String toLowerCase();
接口定義 |
接口使用 |
@FunctionalInterface public interface Supplier<T>{ public T get(); } |
import java.util.function.*; |
④斷言型函數式接口:進行判斷處理
·在String類有一個compareToIgnoreCase()
接口定義 |
接口使用 |
@FunctionalInterface public interface Predicate<T>{ public boolean test(T t); } |
import java.util.function.*; |
以后對於實際開發之中,如果JDK本身提供的函數式接口可以被我們所使用,那么就沒有必要重新定義了。