本篇用代碼的方式并結合自己對Lamba的理解講了Java8的Lambda表達式及函數式接口相關。 參考:http://winterbe.com/posts/2014/03/16/java-8-tutorial/ 1.接口中擴展方法(default關鍵字)package com.mavsplus.java8.turtorial.one;/**
* 公式接口
* <p>
* Java 8 允許我們使用default關鍵字,為接口聲明添加非抽象的方法實現。這個特性又被稱為擴展方法
*
* @author landon
* @since 1.8.0_25
*/public interface Formula { public double calculate(int a); /**
* 默認的非抽象方法(default關鍵字)
*
* @param a
* @return
*/ default double sqrt(int a) { return Math.sqrt(a); } /**
* 增加了public 關鍵字
*
* @param a
* @return
*/ public default double sin(int a) { return Math.sin(a); } /**
* 測試該特性
* <p>
* Run As Java Application的時候會出現兩個選擇,因為除了Formula接口類,還有一個內部類Formula$1
*
* @param args
*/ public static void main(String
args) { // 匿名內部類的方式
Formula formula = new Formula() { @Override public double calculate(int a) { // 調用了默認的非抽象方法
return sqrt(a * 100); } }; System.out.println(formula.calculate(144)); // 直接調用默認的非抽象方法
System.out.println(formula.sqrt(225)); }} 2.Lamba表達式小試牛刀package com.mavsplus.java8.turtorial.lambda;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* 字符串列表排序,使用java8之前實現及java8 lambda實現
*
* @author landon
* @since 1.8.0_25
*/
public class ListOfStringSort {
private List<String> names = Arrays.asList("landon", "kobe", "dirk", "tmac");
/**
* 舊版本實現,使用匿名內部類
*/
public void oldVersionSort() {
Collections.sort(names, new Comparator<String>() {
@Override
public int compare(String o1, String o2) {
return o2.compareTo(o1);
}
});
}
/**
* 使用Lambda表達式實現1
* <p>
* 從語法上看,(String o1, String o2)為Comparator接口中compare方法的兩個參數,沒有方法名; -> 為方法的實現
* 此表達式返回一個Comparator對象
* (編譯器知道Collection.sort第二個參數是一個Comparator接口實現的對象,而該接口只有一個方法compare
* ->個人認為編譯器解釋該lambda表達式的時候也會轉為匿名內部類的方式實現)
*/
public void lambdaSort1() {
Collections.sort(names, (String o1, String o2) -> {
return o2.compareTo(o1);
});
}
/**
* 使用Lambda表達式實現2
* <p>
* 這個實現更加簡單,較第一個版本沒有了大括號和return(個人認為因為編譯器能推斷出返回類型)
*/
public void lambdaSort2() {
Collections.sort(names, (String o1, String o2) -> o2.compareTo(o1));
}
/**
* 使用Lambda表達式實現3
* <p>
* 這是是最簡單的實現,類型都可以省略不寫.因為Java編譯器能夠自動識別參數的類型(注,用ide編輯代碼的時候,敲o2.的時候,
* 自動會出現String的方法_智能提示)
*/
public void lambdaSort3() {
Collections.sort(names, (o1, o2) -> o2.compareTo(o1));
}
}
3.函數式接口(@FunctionalInterface)package com.mavsplus.java8.turtorial.lambda;/**
* Lambda函數式接口
*
* <pre>
* Lambda表達式如何匹配Java的類型系統?
* 每一個lambda都能夠通過一個特定的接口,與一個給定的類型進行匹配。
* 一個所謂的函數式接口必須要有且僅有一個抽象方法聲明,每個與之對應的lambda表達式必須要與抽象方法的聲明相匹配。
* 由于默認方法不是抽象的,因此你可以在你的函數式接口里任意添加默認方法。
* 任意只包含一個抽象方法的接口,我們都可以用來做成lambda表達式。
* 為了讓你定義的接口滿足要求,你應當在接口前加上@FunctionalInterface標注。
* 編譯器會注意到這個標注,如果你的接口中定義了第二個抽象方法的話,編譯器會拋出異常。
* </pre>
*
* <pre>
* @FunctionalInterface public interface Runnable {}
* @FunctionalInterface public interface Comparator<T> {}
*
* 從JDK 1.8的源碼可以看到,一些常用接口都加上了@FunctionalInterface關鍵字,即都是函數式接口->都可以用Lambda表達式進行匹配
* </pre>
*
* @author landon
* @since 1.8.0_25
*/public class LambdaFunctionalInterface { /**
* 自定義的一個函數式接口.->轉換器,將F(From)類型轉為T(Target)類型
*
* @param <F>
* @param <T>
*/ @FunctionalInterface public interface Converter<F, T> { T convert(F from); } public static void main(String
args) { // 使用lamba表達式1,(from)為convert參數列表 -> 方法實現
Converter<String, Integer> converter = (from) -> Integer.valueOf(from); System.out.println(converter.convert("123")); // 使用lambda表達式2,這里加上了大括號和返回值,這樣更直觀一些,->左邊為參數列表,右邊為方法實現
Converter<String, Integer> converter2 = (String from) -> { return Integer.valueOf(from); }; System.out.println(converter2.convert("456")); // 使用lambda表達式3,這里和第一個的區別是加上了參數類型
Converter<String, Integer> converter3 = (String from) -> Integer.valueOf(from); System.out.println(converter3.convert("789")); // 注:本例的Converter接口也可以不加@FunctionalInterface這個注解,因為內部本身只有一個抽象方法
}} 4.::關鍵字獲取方法或者構造函數的的引用(::)package com.mavsplus.java8.turtorial.lambda;
import com.mavsplus.java8.turtorial.lambda.LambdaFunctionalInterface.Converter;
/**
* 方法和構造函數引用
*
* <pre>
* Java 8 允許你通過::關鍵字獲取方法或者構造函數的的引用
* </pre>
*
* @author landon
* @since 1.8.0_25
*/
public class MethodConstructorRef {
/**
* 引用靜態方法
*/
public void refStaticMethod() {
// 使用靜態方法引用::,賦值(個人認為編譯器會處理一切-,>因為是函數式接口)
// 該種方式是相當于通過右側方法引用實現了函數式接口的實現.
Converter<String, Integer> converter = Integer::valueOf;
System.out.println(converter.convert("365"));
}
/**
* 定義一個內部類
*/
public class Something {
/**
* 定義了一個參數是字符串,返回值也是字符串的方法
*
* @param s
* @return
*/
public String startsWith(String s) {
return String.valueOf(s.charAt(0));
}
}
/**
* 對一個對象的方法進行引用
*/
public void refObjectMethod() {
Something something = new Something();
// 將something對象的startsWith方法引用賦值::
// 該種方式是相當于通過右側方法引用實現了函數式接口的實現.
Converter<String, String> converter = something::startsWith;
System.out.println(converter.convert("Lambda"));
}
/**
* 自定義的一個JavaBean
*/
public class Person {
private String firstName;
private String lastName;
public Person() {
}
public Person(String fName, String lName) {
firstName = fName;
lastName = lName;
}
public String getFirstName() {
return firstName;
}
public String getLastName() {
return lastName;
}
}
/**
* 自定義了一個接口,接口內部有兩個字符串參數的方法
*
* @param <P>
*/
public interface PersonFactory<P extends Person> {
public P create(String firstName, String lastName);
}
/**
* 通過::對構造方法進行引用
* Person::new來創建一個Person類構造函數的引用。Java編譯器會自動地選擇合適的構造函數來匹配PersonFactory
* .create函數的簽名,并選擇正確的構造函數形式
*/
public void refConstructor() {
PersonFactory<Person> factory = Person::new;
Person p = factory.create("kobe", "bryant");
System.out.println(p.getFirstName() + "_" + p.getLastName());
}
/**
* 這里是想測試一下,如果接口內兩個抽象方法,能否使用方法引用匹配,因為Person確實是有兩個構造方法
* 結論:不行,方法引用只能是functional
* interface.(個人認為這樣也是非常合理的->因為右側的方法引用只能是確定的一個方法,不可能匹配對個抽象方法
* --雖然構造方法可以有多個,但畢竟是
* ::的特例,特例不能決定全部,而從實現的角度來看,會大大加大編譯器的處理難度,而且只能是針對引用構造方法對多個抽象方法進行特殊處理
* .——————當然同一個方法也有多種不同的重載形式
* ,不過會大大加大編譯器的處理難度-------另一個原因我認為可能是為了要和lambda表達式處理一致
* .lambda表達式本來就是要處理簡單,它不可能在一個表達式中實現多個抽象方法)
*/
public interface PersonFactory2<P extends Person> {
public P create(String firstName, String lastName);
public P create();
}
// 這里出現編譯錯誤:The target type of this expression must be a functional
// interface
public void refConstructor2() {
// PersonFactory2<Person> factory = Person::new;
}
public static void main(String[] args) {
MethodConstructorRef ref = new MethodConstructorRef();
ref.refStaticMethod();
ref.refObjectMethod();
ref.refConstructor();
}
}
5.Lambda表達式訪問范圍 package com.mavsplus.java8.turtorial.lambda;
import com.mavsplus.java8.turtorial.lambda.LambdaFunctionalInterface.Converter;
/**
* Lambda的范圍
* <p>
* 對于lambdab表達式外部的變量,其訪問權限的粒度與匿名對象的方式非常類似。你能夠訪問局部對應的外部區域的局部final變量_enclosing(
* 注_針對局部變量, 而對于外部類變量[成員變量]則無此限制 by landon),以及成員變量和靜態變量
*
* @author landon
* @since 1.8.0_25
*/
public class LambdaRange {
/**
* Lambda訪問Final局部變量
*/
public void lambdaAccessLocalFinalVar() {
final int num = 1;
// 這里直接訪問num
Converter<Integer, String> converter = (from) -> String.valueOf(from + num);
// 輸出3
System.out.println(converter.convert(2));
}
/**
* Lambda訪問非Final局部變量
* <p>
* 但是與匿名對象不同的是,變量num并不需要一定是final.
*/
public void lambdaAccessLocalNonFinalVar() {
int num2 = 2;
// 這里直接訪問num2
// num2在編譯的時候被隱式地當做final變量來處理
Converter<Integer, String> converter = (from) -> String.valueOf(from + num2);
// 輸出4
System.out.println(converter.convert(2));
}
/**
* Lambda嘗試改變非final局部變量
* <p>
* 在lambda表達式內部企圖改變num3的值也是不允許的(因為隱式做final處理)
*/
public void lambdaModifyLocalNonFinalVar() {
int num3 = 3;
Converter<Integer, String> converter = (from) -> String.valueOf(from + num3);
converter.convert(3);
// 加上這句賦值,修改了非final局部變量num3,則編譯錯誤
// 報編譯錯誤:Local variable num3 defined in an enclosing scope must be
// final or effectively final
// num3 = 4;
}
private int outerNum = 4;
private static int outerStaticNum = 5;
/**
* Lambda訪問成員變量
* <p>
* 與局部變量不同,我們在lambda表達式的內部能獲取到對成員變量或靜態變量的讀寫權。這種訪問行為在匿名對象里是非常典型的 ---此規則同匿名對象
*/
public void lambdaAccessMemberAndStaticVar() {
// 訪問外部成員變量
Converter<Integer, String> converter1 = (from) -> {
outerNum = 8;
return String.valueOf(from);
};
converter1.convert(4);
// 訪問外部靜態成員變量
Converter<Integer, String> converter2 = (from) -> {
outerStaticNum = 11;
return String.valueOf(from);
};
converter2.convert(5);
}
/**
* lambda訪問默認方法
* <p>
* 默認方法無法在lambda表達式內部被訪問
*/
public void lambdaAccessDefaultInterfaceMethod() {
// Formula是一個接口,接口中一個抽象的calculate方法和一個默認方法sqrt
// 這里編譯錯誤,The method sqrt(int) is undefined for the type LambdaRange
// 從上面的編譯錯誤可以看出,編譯器根本是不知道sqrt這個方法的存在,它在當前類中也沒有找到該方法的聲明
// 這樣做是非常合理的->編譯器第一步是要先確認-> 右邊的這個方法表達式是否存在(不可能先去接口里是不是默認方法->實現復雜度加大)
// 而用匿名類對象當然可以,因為匿名類就是實現了該接口并繼承了默認方法,按照類繼承的規則當然可以進行訪問
// Formula formula = (a) -> sqrt(a * 100);
}
public static void main(String[] args) {
LambdaRange range = new LambdaRange();
range.lambdaAccessLocalFinalVar();
range.lambdaAccessLocalNonFinalVar();
System.out.println("pre_OuterNum:" + range.outerNum);
System.out.println("pre_OuterStaticNum:" + outerStaticNum);
// 此方法會改變成員變量和靜態變量的值_lambda表達式對其進行了修改
range.lambdaAccessMemberAndStaticVar();
System.out.println("post_OuterNum:" + range.outerNum);
System.out.println("post_OuterStaticNum:" + outerStaticNum);
}
}
6.Java8內置的函數式接口package com.mavsplus.java8.turtorial.lambda;
import java.util.Comparator;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;
import com.mavsplus.java8.turtorial.lambda.MethodConstructorRef.Person;
/**
* Java8內置函數式接口
*
* <pre>
* JDK 1.8 API中包含了很多內置的函數式接口。有些是在以前版本的Java中大家耳熟能詳的,例如Comparator接口,或者Runnable接口。
* 對這些現成的接口進行實現,可以通過@FunctionalInterface 標注來啟用Lambda功能支持。
* 此外,Java 8 API 還提供了很多新的函數式接口,來降低程序員的工作負擔。有些新的接口已經在Google Guava庫中很有名了。
* 如果你對這些庫很熟的話,你甚至閉上眼睛都能夠想到,這些接口在類庫的實現過程中起了多么大的作用.
* </pre>
*
* <p>
* 可以搜索FunctionalInterface的引用,發現諸如Runnable,Comparator,Callbale等接口在JDK8中均加了該注解。
* 另外可以看到在java.util中增加了一個function
* package,即java.util.function,該package即內置了很多函數式接口.
*
* @author landon
* @since 1.8.0_25
*/
public class LambdaBuildInFunctionalInterface {
/**
* Predicate是一個布爾類型的函數,該函數只有一個輸入參數。Predicate接口包含了多種默認方法,用于處理復雜的邏輯動詞(and,
* or,negate_(not)),可以查看其源碼
*
* @FunctionalInterface public interface Predicate<T>{ boolean test(T t);
*/
public void usePredicate() {
// 實現了抽象方法test
Predicate<String> predicate = (s) -> s.length() > 0;
// 調用了test方法
// 輸出:true
System.out.println(predicate.test("landon"));
// default Predicate<T> negate() {return (t) -> !test(t);}
// 可以看到negate方法是一個默認方法,其實現是返回一個!test的Predicate接口
// 輸出:false
System.out.println(predicate.negate().test("landon"));
// Objects是Java7引入的util中的一個類
// public static boolean nonNull(Object obj) { return obj != null;}
Predicate<Boolean> nonNull = Objects::nonNull;
// public static boolean isNull(Object obj) {return obj == null;}
Predicate<Boolean> isNull = Objects::isNull;
// 輸出:true
System.out.println(nonNull.test(true));
// 輸出:false
System.out.println(nonNull.test(null));
// 輸出:false
System.out.println(isNull.test(false));
// 輸出:true
System.out.println(isNull.test(null));
// 這里看到,即使isEmpty方法是String的非靜態方法,這里也可以通過::進行調用.
Predicate<String> isEmpty = String::isEmpty;
Predicate<String> isNotEmpty = isEmpty.negate();
// 輸出:true
System.out.println(isEmpty.test(""));
// 輸出:false
System.out.println(isEmpty.test("lambda"));
// 輸出:false
System.out.println(isNotEmpty.test(""));
// 輸出:true
System.out.println(isNotEmpty.test("lambda"));
}
/**
* Function接口接收一個參數,并返回單一的結果。默認方法可以將多個函數串在一起(compse, andThen)
*
* @FunctionalInterface public interface Function<T, R> {R apply(T t);
*/
public void useFunction() {
// 實現apply接口
Function<String, Integer> toInteger = Integer::valueOf;
// default <V> Function<T, V> andThen(Function<? super R, ? extends V>
// after) {Objects.requireNonNull(after);return (T t) ->
// after.apply(apply(t));}
// andthen方法將apply的的輸出作為再一次apply的輸入,toInteger的apply輸出為Integer->
// 參數fucntion為Function<Integer,String>
Function<String, String> backToString = toInteger.andThen(String::valueOf);
// 輸出:123
System.out.println(backToString.apply("123"));
}
/**
* Supplier接口產生一個給定類型的結果。與Function不同的是,Supplier沒有輸入參數。
*
* @FunctionalInterface public interface Supplier<T> {T get();}
*/
public void useSupplier() {
// 編譯錯誤: No enclosing instance of the type MethodConstructorRef is
// accessible in scope
// 因為Person屬于內部類,其正常的初始化方式應該是先初始化外部類,所以這里并非直接用::new,否則違背了內部類的規則
// Supplier<Person> personSupplier = Person::new;
Supplier<Person> personSupplier = new PersonSupplier();
System.out.println(personSupplier.get().getFirstName());
System.out.println(personSupplier.get().getLastName());
// 這里直接用::new的方式
Supplier<SupplierBean> beanSupplier = SupplierBean::new;
System.out.println(beanSupplier.get().a);
}
// 注:因為Person是MethodConstructorRef的內部類,所以無法使用::new進行初始化
// 所以這里實現了Supplier接口,并采用內部類的方式進行初始化
private class PersonSupplier implements Supplier<Person> {
@Override
public Person get() {
MethodConstructorRef ref = new MethodConstructorRef();
return ref.new Person("java8", "tutorial");
}
}
/**
* 測試Supplier接口而實現的一個JavaBean
*/
private class SupplierBean {
public int a = 10086;
}
/**
* Consumer代表了在一個輸入參數上需要進行的操作
*
* @FunctionalInterface public interface Consumer<T> {void accept(T t);
*/
public void useConsumer() {
// 實現了accept方法,該方法為void
Consumer<Person> greeter = (p) -> System.out.println("Hello," + p.getFirstName());
// 首先實例化外部類
MethodConstructorRef ref = new MethodConstructorRef();
greeter.accept(ref.new Person("Java8", "Lambda"));
}
/**
* 使用加了@FunctionalInterface的Comparator. Java 8 為這個接口添加了不同的默認方法
*/
public void useNewComparator() {
// 實現compare接口
Comparator<Person> comparator = (p1, p2) -> p1.getFirstName().compareTo(p2.getFirstName());
// 首先實例化外部類
MethodConstructorRef ref = new MethodConstructorRef();
Person p1 = ref.new Person("tracy", "mcgrady");
Person p2 = ref.new Person("kobe", "bryant");
System.out.println(comparator.compare(p1, p2));
// default Comparator<T> reversed() {return
// Collections.reverseOrder(this);}
// reversed方法為Comparator接口中的一個默認方法.
System.out.println(comparator.reversed().compare(p1, p2));
}
/**
* Optional不是一個函數式接口,而是一個精巧的工具接口,用來防止NullPointerEception產生。
* Optional是一個簡單的值容器,這個值可以是null,也可以是non-null。考慮到一個方法可能會返回一個non-null的值,
* 也可能返回一個空值。為了不直接返回null,我們在Java 8中就返回一個Optional。
*
* public final class Optional<T>
*/
public void useOptional() {
// public static <T> Optional<T> of(T value) { return new
// Optional<>(value);}
Optional<String> optional = Optional.of("landon");
// public boolean isPresent() {return value != null;}
System.out.println(optional.isPresent());
// public T get() {if (value == null) {throw new
// NoSuchElementException("No value present");}return value;}
System.out.println(optional.get());
// public T orElse(T other) {return value != null ? value : other;}
System.out.println(optional.orElse("mavs"));
// public void ifPresent(Consumer<? super T> consumer) {if (value !=
// null)consumer.accept(value);}
optional.ifPresent((s) -> System.out.println(s.charAt(0)));
}
public static void main(String[] args) {
LambdaBuildInFunctionalInterface buildInFunctionalInterface = new LambdaBuildInFunctionalInterface();
buildInFunctionalInterface.usePredicate();
buildInFunctionalInterface.useFunction();
buildInFunctionalInterface.useSupplier();
buildInFunctionalInterface.useConsumer();
buildInFunctionalInterface.useNewComparator();
buildInFunctionalInterface.useOptional();
}
}
posted on 2014-11-17 15:45
landon 閱讀(5953)
評論(0) 編輯 收藏 所屬分類:
Program