天天看點

java8 新特性 lambad表達式

1.介紹

java8提供了預設接口方法,Lambda表達式,方法引用和重複注解等新的特性及API

2.允許在接口中有預設方法實作

Java 8 允許我們使用default關鍵字,為接口聲明添加非抽象的方法實作。這個特性又被稱為擴充方法:

舉例:

public interface PhoneService {
    void call();
    //接口中預設方法實作
    default String who(String name) {
        return "name"+name;
    }
}
           

3.lambad表達式

java中lambad表達式的實作有兩個結構。一個是lambad表達式自身。另一個是函數式接口。

lambad表達式本質上是一個匿名(未命名)方法。

函數式接口是僅包含一個抽象方法的接口。如Runnable接口

lambad表達式文法

(0個或多個參數) -> 語句塊

如:() -> 3.14 ; (n) -> {return n*10;}

3.1 lambad 函數式接口

把一個lambad表達式賦給一個函數式接口的引用時。會自動建立實作函數式接口的一個類的執行個體。

舉例:

定義一個函數式接口:

public interface PhoneService {
    void call();

    default String who(String name) {
        return "name"+name;
    }
}
           

測試:

public class LambadTest {
    public static void main(String[] args) {
        PhoneService service = () -> {System.out.println("call");};
        service.call();
    }
}
           

lambad表達式的參數要麼顯示指定。要麼程式可以根據上下文做隐式的推斷。

舉例:

public interface DogService {
    String who(String name);
}

public class LambadTest {
    public static void main(String[] args) {
        //單個表達式 可以省略{}
        DogService service = (name) ->  "name is:" + name;

        //根據上下文推斷上下文類型;多個表達式要指定{}
        DogService service1 = (name) -> {
            System.out.println("wangwang");
            return "name is:" + name;
        };
        //強制指定參數類型
        DogService service2 = (String name) -> {
            return "name is:" + name;
        };

        //抽象方法隻有一個參數時可以忽略()括号
        DogService service3 = name -> {
            return "name is" + name;
        };

        System.out.println(service.who("haha"));
        System.out.println(service1.who("xiaozhang"));
        System.out.println(service2.who("xiaoma"));
        System.out.println(service3.who("xiaowang"));
    }
}
           

3.2 lambad 泛型函數式接口

lambad表達式自身不能指定類型參數。是以lambad表達式不能是泛型。但是與lambad是關聯的函數式接口的參數可以是泛型。此時,lambad表達式中的參數有調用接口時指定的參數類型來決定具體的類型。

舉例:

public interface SomeFunc<T> {
    T fun(T t);
}

public class FanTest {
    public static void main(String[] args) {
        SomeFunc strFun = (str) -> str +"";

        System.out.println(strFun.fun());
        System.out.println("hello");
    }
}
           

3.3 作為參數傳遞lambad表達式

把lambad表達式作為參數傳給函數體。這是lambad式最常見的用途。也是lambad最強大的表達式,因為可以将可執行代碼塊最為參數傳遞給方法。這極大的增強了java的表達能力。

為了将lambad表達式作為參數傳遞。接收lambad表達式的參數類型必須是與該lambad表達式相容的函數式接口的類型。

舉例:

public interface StringFunc {
    String func(String n);
}

public class LambadAsArgumentDemo {
    static String stringOp(StringFunc sf,String s) {
        return  sf.func(s);
    }

    public static void main(String[] args) {
        String result = stringOp((n) -> n.toUpperCase(),"hello");
        System.out.println(result);
    }
}
           

3.3 lambad表達式與異常

lambad表達式可以抛出異常。但是,如果抛出經檢查異常,該異常必須與函數式接口的抽象方法的throws字句中列出的異常相容。

舉例:

public interface DoubleNumbicArrayFunc {
    double func(double[] n) throws  IllegalArgumentException;
}

public class LambadExceptionDemo {
    public static void main(String[] args) {
        double values[] = {,,};
        DoubleNumbicArrayFunc func = n -> {
            if (n.length == ) {
                throw new IllegalArgumentException("array empty");
            }

            double sum = ;
            for (double c:n) {
                sum = sum + c;
            }
            return sum / n.length;
        };

        System.out.println(func.func(values));
        System.out.println(func.func(new double[]));
    }
}
           

3.4 lambad表達式與變量捕獲

lambad表達式可以擷取或設定其外層類的執行個體或靜态變量值。以及調用外層類定義的方法。

但是,當lambad使用其外層作用域定義的局部變量時,會産生一種特殊的情況,稱為變量捕獲。在這種情況下,lambad表達式隻能使用實質上是final的局部變量。不能修改外層作用域的局部變量

舉例:

public interface MyFunc {
    int func(int n);
}

public class MyFuncDemo {
    private int age = ;

    private static final String name = "zhangxj";

    private void printName() {
        System.out.println("my name is zxj");
    }

    public static void main(String[] args) {
        int height = ;

        MyFuncDemo demo = new MyFuncDemo();

        MyFunc func = n -> {
            demo.printName();
            System.out.println(demo.age);
            demo.age = demo.age + ;
            System.out.println(demo.age);
            System.out.println(MyFuncDemo.name);
            System.out.println(height);
            //對height隻能通路。不能修改。否則報錯
            return n + height;
        };
        System.out.println(func.func());
    }

}
           
lambad可以使用和修改其調用類的執行個體變量,隻是不能使用外層作用域内的局部變量,除非該變量實質上是final 變量。

方法引用

方法引用提供了一種引用而不執行方法的方式。這種特性與lambad表達式關聯,因為它也需要由相容的函數式接口構成的目标上下文。

函數引用會建立函數式接口的一個執行個體。

3.5 靜态方法的方法引用

ClassName::methodName
public interface StringFunc {
    String func(String n);
}

public class MyStringOps {
    public static String strUppcase(String str) {
        return str.toUpperCase();
    }
}

public class MethodRefDemo {

    static String stringOp(StringFunc sf,String s) {
        return sf.func(s);
    }

    public static void main(String[] args) {
        //靜态方法引用
        String outStr = stringOp(MyStringOps::strUppcase,"lambads add power to java");
        System.out.println(outStr);
    }
}
           

3.6 執行個體方法的引用

objRef::methodName
public interface StringFunc {
    String func(String n);
}

public class MyStringOps {
    public  String strUppcase(String str) {
        return str.toUpperCase();
    }
}

public class MethodRefDemo {

    static String stringOp(StringFunc sf,String s) {
        return sf.func(s);
    }

    public static void main(String[] args) {
        MyStringOps ops = new MyStringOps();
        //引用方法執行個體
        String outStr = stringOp(ops::strUppcase,"lambads add power to java");
        System.out.println(outStr);
    }
}
           

3.7 泛型中的方法引用

在泛型類/泛型方法中。可以使用方法引用

public interface MyFunc<T> {
    int func(T[] vals,T v);
}

public class MyArrayOps {
    static <T> int countMatching(T[] vals,T v) {
        int count = ;

        for (int i = ; i < vals.length;i++) {
            if (vals[i] == v) count++;
        }
        return count;
    }
}


public class GenericMethodRefDemo {
    static <T> int myOp(MyFunc<T> f,T[] vals,T v) {
        return f.func(vals,v);
    }

    public static void main(String[] args) {
        Integer[] vals = {,,,,,,,};

        int result = myOp(MyArrayOps::countMatching,vals,);
        System.out.println(result);
    }
}
           

3.8 構造函數引用

className::new

可以把這個引用指派給定義的方法與構造函數相容的任何函數式接口的引用。

public interface MyFunc {
     MyClass func(int n);
}

public class MyClass {
    private int val;

    public MyClass(int val) {
        this.val = val;
    }

    public MyClass() {
        this.val = ;
    }

    public int getVal() {
        return val;
    }
}

public class ConstructRefDemo {
    public static void main(String[] args) {
        MyFunc myFunc = MyClass::new;

        //建立MyClass的一個執行個體
        MyClass myClass = myFunc.func();

        System.out.println(myClass.getVal());
    }
}
           

參考:Java 8程式設計官方參考教程(第9版)