Java8新特性(函數語言程式設計)
阿新 • • 發佈:2018-11-28
1.介面的預設方法
Java 8允許我們給介面新增一個非抽象的方法實現,只需要使用 default關鍵字即可,這個特徵又叫做擴充套件方法
//Formula表示一個設計 計算公式 的介面
public interface Formula {
//計算
double calculate(int a);
//開方
default double sqrt(int a){
return Math.sqrt(a);
}
}
public static void main(String[] args){
Formula f = new Formula() {
@Override
public double calculate(int a) {
return a+1;
}
};
}
System.out.println(f.calculate(4));
System.out.println(f.sqrt(8));
注意:現在介面還可以存在靜態方法,
可以使用 介面名.靜態方法名 的形式直接呼叫
2. Lambda 表示式
2.1 認識Lambda表示式
例如:
public class LambdaTest1 {
public static void main(String[] args) {
//假如一個list機會中的元素要排序
List<String> list = Arrays.asList
("hello","tom","apple","bbc");
//之前的排序我們可以這樣寫
Collections. sort(list, new Comparator<String>(){
@Override
public int compare(String o1, String o2) {
return -o1.compareTo(o2);
}
});
//使用Lambda表示式
Collections.sort(list,(String s1,String s2)->{
return s1.compareTo(s2);
});
//可以簡寫為
//1.大括號裡面就一句程式碼
//2.編譯器可以自動推匯出引數型別
Collections.sort(list,(s1,s2)->s1.compareTo(s2));
System.out.println(list);
}
}
2.2 Functional介面
“函式式介面”是指僅僅只包含一個抽象方法的介面,每一個該型別的lambda表示式都會被匹配到這個抽象方法。因為 預設方法 不算抽象方法,所以你也可以給你的函式式介面新增預設方法。
我們可以將lambda表示式當作任意只包含一個抽象方法的介面型別,確保你的介面一定達到這個要求,你只需要給你的介面新增 @FunctionalInterface 註解,編譯器如果發現你標註了這個註解的介面有多於一個抽象方法的時候會報錯的。
例如:
public class LambdaTest2 {
public static void main(String[] args) {
LambdaTest2 t = new LambdaTest2();
// 也可以先建立物件
// Action1 a1 = ()->System.out.println("hello");
t.test1(()->System.out.println("hello"));
//Action2<String,Integer> a2 = (f)->"這個數字是:"+f;
//如果引數就一個,那麼還可以這樣簡寫 去掉小括號
Action2<String,Integer> a2 = f->"這個數字是:"+f;
t.test2(a2);
}
public void test1(Action1 a){
a.run();
}
public void test2(Action2<String,Integer> a){
System.out.println(a.run(3));
}
}
//這個註解不加也可以,加上只是為了讓編譯器檢查
@FunctionalInterface
interface Action1{
public void run();
}
//這個註解不加也可以,加上只是為了讓編譯器檢查
@FunctionalInterface
interface Action2<T,F>{
public T run(F f);
}
注意:lambda表示式無法訪問介面的預設方法
2.3 方法與建構函式引用
Java 8 允許你使用 :: 關鍵字來傳遞方法(靜態方法和非靜態方法)
例如:
public class LambdaTest3 {
public static void main(String[] args) {
LambdaTest3 t = new LambdaTest3();
//使用Lambda引用類的靜態方法
//能引用Integer類中的靜態方法toBinaryString的原因是:
//Action3介面中只有一個方法且方法的引數型別和返回值型別
//與Integer類中的靜態方法toBinaryString的引數型別、返回型別是一致的
Action3 a3 = Integer::toBinaryString;
System.out.println(a3.run(4));
//使用Lambda引用物件的非靜態方法
//能引用物件t中的非靜態方法test的原因是和上面的描述是一致的
Action3 aa3 = t::test;
System.out.println(aa3.run(4));
}
public String test(int i){
return "i="+i;
}
}
@FunctionalInterface
interface Action3{
public String run(int Integer);
}
下面是一個介面中帶泛型的時候特殊例子: 可以使用 類名::非靜態方法 的形式引用方法
public class LambdaTest6 {
public static void main(String[] args) {
Model m = new Model();
//方法有一個引數,然後沒返回型別,這裡引數型別會自動識別
Action<Model> a1 = (s)->System.out.println("hello");
a1.run(m);
//注意:如果這裡泛型型別不是Model 那麼就不能引用Model中的方法
//可以引用Model類中任意方法 只要滿足一點:該方法沒有引數
//將來run方法中就會呼叫Model型別物件m的此處引用的方法
Action<Model> a2 = Model::test3;
a2.run(m);
//引用物件m中的test2方法
//因為test2方法的引數和返回型別和Action介面的方法完全一致
Action<Model> a3 = m::test2;
a3.run(m);
}
}
interface Action<T>{
public void run(T t);
}
class Model{
public void test1(){
System.out.println("test1");
}
public void test2(Model a){
System.out.println("test2");
}
public int test3(){
System.out.println("test3");
return 1;
}
}
Java 8 允許你使用 :: 關鍵字來引用建構函式
public class LambdaTest4 {
public static void main(String[] args) {
//Lambda表示式引用建構函式
//根據構造器的引數來自動匹配使用哪一個構造器
Action4Creater creater = Action4::new;
Action4 a4 = creater.create("zhangsan");
a4.say();
}
}
class Action4{
private String name;
public Action4() {
}
public Action4(String name) {
this.name = name;
}
public void say(){
System.out.println("name = "+name);
}
}
interface Action4Creater{
public Action4 create(String name);
}
2.4 lambda表示式中的變數訪問
public class LambdaTest5 {
private static int j;
private int k;
public static void main(String[] args) {
LambdaTest5 t = new LambdaTest5();
t.test();
}
public void test(){
int num = 10;
j = 20;
k = 30;
//lambda表示式中可以訪問成員變數也可以方法區域性變數
Action5 a5 = (i)->System.out.println("操作後:i="+(i+num+j+k));
a5.run(1);
//但是這個被訪問的變數預設變為final修飾的 不可再改變 否則編譯不通過
//num = 60;
j = 50;
k = 70;
}
}
interface Action5{
public void run(int i);
}
2.5 Predicate介面和lambda表示式
java.util.function.Predicate介面是用來支援java函數語言程式設計新增的一個介面,使用這個介面和lamb表示式就可以以更少的程式碼為API方法新增更多的動態行為。
public class LambdaTest6 {
public static void main(String[] args) {
List<String> languages = Arrays.asList("Java", "html5","JavaScript", "C++", "hibernate", "PHP");
//開頭是J的語言
filter(languages,(name)->name.startsWith("J"));
//5結尾的
filter(languages,(name)->name.endsWith("5"));
//所有的語言
filter(languages,(name)->true);
//一個都不顯示
filter(languages,(name)->false);
//顯示名字長度大於4
filter(languages,(name)->name.length()>4);
System.out.println("-----------------------");
//名字以J開頭並且長度大於4的
Predicate<String> c1 = (name)->name.startsWith("J");
Predicate<String> c2 = (name)->name.length()>4;
filter(languages,c1.and(c2));
//名字不是以J開頭
Predicate<String> c3 = (name)->name.startsWith("J");
filter(languages,c3.negate());
//名字以J開頭或者長度小於4的
Predicate<String> c4 = (name)->name.startsWith("J");
Predicate<String> c5 = (name)->name.length()<4;
filter(languages,c4.or(c5));
//名字為Java的
filter(languages,Predicate.isEqual("Java"));
//判斷倆個字串是否相等
boolean test = Predicate.isEqual("hello").test("world");
System.out.println(test);
}
public static void filter(List<String> languages, Predicate<String> condition) {
for(String name: languages) {
if(condition.test(name)) {
System.out.println(name + " ");
}
}
}
}
2.6 Function 介面
Function有一個引數並且返回一個結果,並附帶了一些可以和其他函式組合的預設方法
compose方法表示在某個方法之前執行
andThen方法表示在某個方法之後執行
注意:compose和andThen方法呼叫之後都會把物件自己本身返回,這可以方便鏈式程式設計
default <V> Function<T,V> andThen(Function<? super R,? extends V> after) 返回一個先執行當前函式物件apply方法再執行after函式物件apply方法的函式物件。
default <V> Function<T,V> compose(Function<? super V,? extends T> before)返回一個先執行before函式物件apply方法再執行當前函式物件apply方法的函式物件。
static <T> Function<T,T> identity() 返回一個執行了apply()方法之後只會返回輸入引數的函式物件。
public interface Function<T, R> {
R apply(T t);
default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
Objects.requireNonNull(before);
return (V v) -> apply(before.apply(v));
}
default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
Objects.requireNonNull(after);
return (T t) -> after.apply(apply(t));
}
//注意: t->t是(t)->t的簡寫
//t->t是作為方法identity的返回值的,也就是Function型別物件
//類似於這樣的寫法:Function<Object, Object> f = t->t;
//那麼f.apply("test") 返回字串"test"
//傳入什麼則返回什麼
static <T> Function<T, T> identity() {
return t -> t;
}
}
例如:
public class LambdaTest7 {
//靜態內部類
private static class Student{
private String name;
public Student(String name){
this.name = name;
}
public String getName() {
return name;
}
}
public static void main(String[] args) {
/*使用者註冊輸入一個名字tom*/
String name = "tom";
/*使用使用者的輸入的名字建立一個物件*/
Function<String, Student> f1 =(s)->new Student(s);
//注意上面的程式碼也可以寫出這樣,引用類中的構造器
//Function<String, Student> f1 =Student::new;
Student stu1 = f1.apply(name);
System.out.println(stu1.getName());
/*需求改變,使用name建立Student物件之前需要給name加一個字首*/
Function<String,String> before = (s)->"briup_"+s;
//表示f1呼叫之前先執行before物件的方法,把before物件的方法返回結果作為f1物件方法的引數
Student stu2 = f1.compose(before).apply(name);
System.out.println(stu2.getName());
/*獲得建立好的物件中的名字的長度*/
Function<Student,Integer> after = (stu)->stu.getName().length();
//before先呼叫方法,結果作為引數傳給f1來呼叫方法,結果再作為引數傳給after,結果就是我們接收的資料
int len = f1.compose(before).andThen(after).apply(name);
System.out.println(len);
}
}
2.7 Supplier介面
Supplier介面返回一個任意範型的值,和Function介面不同的是該介面沒有任何引數
public interface Supplier<T> {
T get();
}
例如:
public class LambdaTest8 {
public static void main(String[] args) {
//生成一個八位的隨機字串
Supplier<String> f = ()->