2014年java8就已經(jīng)被提出來了,越來越多的公司項(xiàng)目也都開始使用了Lambdas表達(dá)式,所以本著學(xué)習(xí)的角度,寫一篇主體是Lambdas的文章。
先講一些java8能用到的新特性:
- 接口當(dāng)中寫實(shí)現(xiàn)方法,只需要添加一個(gè)default的關(guān)鍵字
public interface IInterface {
default void init(){//default要求最低版本要24 所以基本沒用
Log.i("dargon","init");
}
}
static int create(int i){//還可以在接口中設(shè)置靜態(tài)方法
return 1;
}
- Lambdas表達(dá)式,Lambdas寫出來的代碼非常簡潔,漂亮,他允許開發(fā)者將方法的引用當(dāng)做參數(shù)傳進(jìn)去,但是由此會(huì)帶來閱讀性下降,并且需要了解Lambdas才能看懂他的代碼。
- 函數(shù)式接口,其實(shí)目的也是為了更好的支持lambdas表達(dá)式。函數(shù)式接口是只包含一個(gè)抽象方法的接口,可以加注解@FunctionalInterface ,編譯器會(huì)注意到這個(gè)標(biāo)注,如果你的接口中定義了第二個(gè)抽象方法的話,編譯器會(huì)拋出異常。可以不加。
@FunctionalInterface //可以不加
public interface IConverter<F,T> {
T convert(F f);
}
IConverter<String,Integer> converter = (from) -> Integer.valueOf(from);
converter.convert("123");//返回123
//說的清楚一點(diǎn)就是創(chuàng)建了一個(gè)實(shí)現(xiàn)這個(gè)接口的子類,實(shí)現(xiàn)convert方法,之后就可以直接調(diào)用方法了,就是寫的非常簡潔一點(diǎn),編譯器會(huì)自動(dòng)適配到相應(yīng)的方法的。
可以寫的更加簡潔一點(diǎn):
IConverter<String,Integer> converter = Integer::valueOf;//Java 8 允許你通過::關(guān)鍵字獲取方法或者構(gòu)造函數(shù)的的引用。
如果還是不清楚 ,可以看這個(gè)三個(gè)代碼塊,從原型到精簡版:
List<String> names = Arrays.asList("peter", "anna", "mike", "xenia");
Collections.sort(names, new Comparator<String>() {//原型
@Override
public int compare(String a, String b) {
return b.compareTo(a);
}
});
Collections.sort(names, (String a, String b) -> { return b.compareTo(a);});//精簡版1
//當(dāng)只有一行代碼的時(shí)候 你甚至可以不要return 跟大括號(hào)
Collections.sort(names,(String a, String b) -> b.compareTo(a));//精簡版2
Collections.sort(names, (a, b) -> b.compareTo(a));//最終版
- ::符號(hào),在java8中被定義了新的特性,可以直接引用已有java類或?qū)ο蟮姆椒ɑ驑?gòu)造器。
第一種方法引用是構(gòu)造器引用,它的語法是Class::new,或者更一般的Class< T >::new。請(qǐng)注意構(gòu)造器沒有參數(shù)。
final Car car = Car.create( Car::new );
final List< Car > cars = Arrays.asList( car );
第二種方法引用是靜態(tài)方法引用,它的語法是Class::static_method。請(qǐng)注意這個(gè)方法接受一個(gè)Car類型的參數(shù)。
cars.forEach( Car::collide );
第三種方法引用是特定類的任意對(duì)象的方法引用,它的語法是Class::method。請(qǐng)注意,這個(gè)方法沒有參數(shù)。
cars.forEach( Car::repair );
最后,第四種方法引用是特定對(duì)象的方法引用,它的語法是instance::method。請(qǐng)注意,這個(gè)方法接受一個(gè)Car類型的參數(shù)
final Car police = Car.create( Car::new );
cars.forEach( police::follow );
- 重復(fù)注解:之前注解在一個(gè)地方只能聲明一次,java8可以使相同的注解在同一個(gè)地方聲明多次,通過數(shù)組的形式。
- 擴(kuò)展注解的支持,java8可以為任何東西添加注解,局部變量、泛型類、父類與接口的實(shí)現(xiàn),就連方法的異常也能添加注解。
- 對(duì)于編譯器的優(yōu)化:java8通過反射可以獲取到參數(shù)的名字
- 類庫的增加:Optional類,通過它來做空指針的判斷
Optional實(shí)際上是個(gè)容器:它可以保存類型T的值,或者僅僅保存null。Optional提供很多有用的方法,這樣我們就不用顯式進(jìn)行空值檢測。更多詳情請(qǐng)參考[官方文檔](http://docs.oracle.com/javase/8/docs/api/)。
我們下面用兩個(gè)小例子來演示如何使用Optional類:一個(gè)允許為空值,一個(gè)不允許為空值。
Optional< String > fullName = Optional.ofNullable(null);
System.out.println("Full Name is set? "+ fullName.isPresent() );
System.out.println("Full Name: "+ fullName.orElseGet( () ->"[none]") );
System.out.println( fullName.map( s ->"Hey "+ s +"!").orElse("Hey Stranger!") );
如果Optional類的實(shí)例為非空值的話,isPresent()返回true,否從返回false。為了防止Optional為空值,orElseGet()方法通過回調(diào)函數(shù)來產(chǎn)生一個(gè)默認(rèn)值。map()函數(shù)對(duì)當(dāng)前Optional的值進(jìn)行轉(zhuǎn)化,然后返回一個(gè)新的Optional實(shí)例。orElse()方法和orElseGet()方法類似,但是orElse接受一個(gè)默認(rèn)值而不是一個(gè)回調(diào)函數(shù)。下面是這個(gè)程序的輸出:
Full Name isset?false
Full Name: [none]
Hey Stranger!
讓我們來看看另一個(gè)例子:
Optional< String > firstName = Optional.of("Tom");
System.out.println("First Name is set? "+ firstName.isPresent() );
System.out.println("First Name: "+ firstName.orElseGet( () ->"[none]") );
System.out.println( firstName.map( s ->"Hey "+ s +"!").orElse("Hey Stranger!") );
System.out.println();
下面是程序的輸出:
First Name isset?true
First Name: Tom
Hey Tom!
還有一個(gè)stream類,他可以很大程度上簡化對(duì)集合的處理步驟跟效率,寫法上跟rxjava其實(shí)很類似。
讓我們以一個(gè)簡單的Task類為例進(jìn)行介紹:
public class Streams {
private enum Status { OPEN, CLOSED };
private static final class Task {
private final Status status;
private final Integer points;
Task( final Status status,final Integer points ) {
this.status = status;
this.points = points;}
public Integer getPoints() { return points;}
public Status getStatus() {
return status;
}
@Override
public String toString() {
return String.format("[%s, %d]", status, points );
}
}
}
Task類有一個(gè)分?jǐn)?shù)的概念(或者說是偽復(fù)雜度),其次是還有一個(gè)值可以為OPEN或CLOSED的狀態(tài).讓我們引入一個(gè)Task的小集合作為演示例子:
final Collection< Task > tasks = Arrays.asList(new Task( Status.OPEN,5),new Task( Status.OPEN,13),new Task( Status.CLOSED,8));
我們下面要討論的第一個(gè)問題是所有狀態(tài)為OPEN的任務(wù)一共有多少分?jǐn)?shù)?在Java 8以前,一般的解決方式用foreach循環(huán),但是在Java 8里面我們可以使用stream:一串支持連續(xù)、并行聚集操作的元素。
// Calculate total points of all active tasks using sum()
final long totalPointsOfOpenTasks = tasks.stream()
.filter( task -> task.getStatus() == Status.OPEN )
.mapToInt( Task::getPoints )
.sum();
System.out.println("Total points: "+ totalPointsOfOpenTasks );
程序在控制臺(tái)上的輸出如下:
Total points: 18
這里有幾個(gè)注意事項(xiàng)。第一,task集合被轉(zhuǎn)換化為其相應(yīng)的stream表示。然后,filter操作過濾掉狀態(tài)為CLOSED的task。下一步,mapToInt操作通過Task::getPoints這種方式調(diào)用每個(gè)task實(shí)例的getPoints方法把Task的stream轉(zhuǎn)化為Integer的stream。最后,用sum函數(shù)把所有的分?jǐn)?shù)加起來,得到最終的結(jié)果。
在繼續(xù)講解下面的例子之前,關(guān)于stream有一些需要注意的地方(詳情[在這里](http://docs.oracle.com/javase/8/docs/api/java/util/stream/package-summary.html#StreamOps)).stream操作被分成了中間操作與最終操作這兩種。
中間操作返回一個(gè)新的stream對(duì)象。中間操作總是采用惰性求值方式,運(yùn)行一個(gè)像filter這樣的中間操作實(shí)際上沒有進(jìn)行任何過濾,相反它在遍歷元素時(shí)會(huì)產(chǎn)生了一個(gè)新的stream對(duì)象,這個(gè)新的stream對(duì)象包含原始stream中符合給定謂詞的所有元素。
像forEach、sum這樣的最終操作可能直接遍歷stream,產(chǎn)生一個(gè)結(jié)果或副作用。當(dāng)最終操作執(zhí)行結(jié)束之后,stream管道被認(rèn)為已經(jīng)被消耗了,沒有可能再被使用了。在大多數(shù)情況下,最終操作都是采用及早求值方式,及早完成底層數(shù)據(jù)源的遍歷。
stream另一個(gè)有價(jià)值的地方是能夠原生支持并行處理。讓我們來看看這個(gè)算task分?jǐn)?shù)和的例子。
// Calculate total points of all tasks
final double totalPoints = tasks .stream() .parallel() .map( task -> task.getPoints() ) // or map( Task::getPoints )
.reduce(0, Integer::sum );
System.out.println("Total points (all tasks): "+ totalPoints );
這個(gè)例子和第一個(gè)例子很相似,但這個(gè)例子的不同之處在于這個(gè)程序是并行運(yùn)行的,其次使用reduce方法來算最終的結(jié)果。下面是這個(gè)例子在控制臺(tái)的輸出:
Total points (all tasks): 26.0
經(jīng)常會(huì)有這個(gè)一個(gè)需求:我們需要按照某種準(zhǔn)則來對(duì)集合中的元素進(jìn)行分組。Stream也可以處理這樣的需求,下面是一個(gè)例子:
// Group tasks by their status
final Map< Status, List< Task > > map = tasks.stream() .collect( Collectors.groupingBy( Task::getStatus ) );
System.out.println( map );
這個(gè)例子的控制臺(tái)輸出如下:
{CLOSED=[[CLOSED, 8]], OPEN=[[OPEN, 5], [OPEN, 13]]}
讓我們來計(jì)算整個(gè)集合中每個(gè)task分?jǐn)?shù)(或權(quán)重)的平均值來結(jié)束task的例子。
// Calculate the weight of each tasks (as percent of total points)
final Collection< String > result = tasks.stream() // Stream< String >
.mapToInt( Task::getPoints ) // IntStream
.asLongStream() // LongStream
.mapToDouble( points -> points / totalPoints ) // DoubleStream
.boxed() // Stream< Double >
.mapToLong( weigth -> (long)( weigth *100) )// LongStream
.mapToObj( percentage -> percentage +"%") // Stream< String>
.collect( Collectors.toList() ); // List< String >
System.out.println( result );
下面是這個(gè)例子的控制臺(tái)輸出:
[19%, 50%, 30%]
最后,就像前面提到的,Stream API不僅僅處理Java集合框架。像從文本文件中逐行讀取數(shù)據(jù)這樣典型的I/O操作也很適合用Stream API來處理。下面用一個(gè)例子來應(yīng)證這一點(diǎn)。
final Path path =new File( filename ).toPath();
try( Stream< String > lines = Files.lines( path, StandardCharsets.UTF_8 ) ) {lines.onClose( () -> System.out.println("Done!") ).forEach( System.out::println );}
對(duì)一個(gè)stream對(duì)象調(diào)用onClose方法會(huì)返回一個(gè)在原有功能基礎(chǔ)上新增了關(guān)閉功能的stream對(duì)象,當(dāng)對(duì)stream對(duì)象調(diào)用close()方法時(shí),與關(guān)閉相關(guān)的處理器就會(huì)執(zhí)行。
還有時(shí)間類Clock,并不做過多介紹了,畢竟代碼還是熟悉的寫起來才快,不是說原生的就是最好的。
如果出現(xiàn)異?;蛘呔?,或者顯示不支持lambdas表達(dá)式,可以參照這篇博客
http://blog.csdn.net/nicolelili1/article/details/52275263
參考資料:
http://www.importnew.com/11908.html
http://blog.csdn.net/chenleixing/article/details/47802675