一.接口內(nèi)允許添加默認(rèn)實(shí)現(xiàn)的方法
在原來(lái)的定義中接口中只能有方法聲明懊直,不能有方法體。在Java8中,接口也可以有自己帶有實(shí)現(xiàn)的方法啦。具體來(lái)說(shuō)是要用default來(lái)修飾的方法喳魏,其可以像類中的方法一樣有執(zhí)行語(yǔ)句。在實(shí)現(xiàn)接口時(shí)怀薛,可以不實(shí)現(xiàn)其default方法刺彩,并且實(shí)現(xiàn)類對(duì)象可以調(diào)用其接口的default方法。當(dāng)然也可以在實(shí)現(xiàn)類中覆蓋default方法枝恋。
public class InterfaceDemo {
public static void main(String[] args) {
Formula formula = new Formula() {
@Override
public double calculate(int a) {
return a + 1;
}
// @Override
// public double sqrt(int a) {
// return a;
// }
};
System.out.println(formula.calculate(100));
System.out.println(formula.sqrt(100));
}
}
interface Formula {
//計(jì)算
double calculate(int a);
//求平方根
default double sqrt(int a) {
return Math.sqrt(a);
}
}
二.Lambda表達(dá)式
Lambda簡(jiǎn)化了匿名內(nèi)部類的寫(xiě)法创倔。Java8中可以通過(guò)類型推斷來(lái)判斷出用戶的意圖,不必將類型等信息寫(xiě)全焚碌。特別是方法實(shí)現(xiàn)體中只有一句語(yǔ)句的實(shí)現(xiàn)類畦攘,更能加大簡(jiǎn)化力度。
Lambda解決了將一個(gè)方法作為參數(shù)傳值的問(wèn)題十电。解決了一個(gè)函數(shù)是否可以獨(dú)立存在的問(wèn)題念搬。是Java向函數(shù)式編程的一種靠攏。
一般在某個(gè)方法只使用一次的地方使用Lambda表達(dá)式摆出;如果方法沒(méi)有入?yún)ⅲ瑒t只寫(xiě)一個(gè)()->{語(yǔ)句}首妖;當(dāng)只有一個(gè)參數(shù)偎漫,且類型可推斷時(shí),()可省略有缆;如果方法體中只有一條語(yǔ)句花括號(hào)可以省略象踊;
public class LambdaDemo {
public static void func1() {
System.out.println("not use Lambda");
List<String> strs = Arrays.asList("hello","word","apple","people","sea","book","school","computer");
//old way
Collections.sort(strs,new Comparator<String>() {
@Override
public int compare(String a,String b) {
return b.compareTo(a);
}
});
System.out.println(strs);
}
public static void func2() {
System.out.println("use Lambda");
List<String> strs = Arrays.asList("hello","word","apple","people","sea","book","school","computer");
//way 1
Collections.sort(strs,(String a,String b) -> {
return a.compareTo(b);
});
//way 2 : only one statement
Collections.sort(strs,(String a,String b) -> a.compareTo(b));
//way 3 : omit class
Collections.sort(strs,(a,b) -> a.compareTo(b));
System.out.println(strs);
}
}
三.函數(shù)式接口Functional Interface
學(xué)習(xí)了上述Lambda的內(nèi)容肯定會(huì)有一些疑問(wèn):如果接口有多個(gè)需要實(shí)現(xiàn)的方法 呢,還能使用Lambda棚壁?如果可以的話Lambda是如何做推斷的杯矩。
答案是,使用Lambda時(shí)要求接口中只能有一個(gè)抽象方法(通過(guò)default修飾的帶有方法體的接口中的方法不是抽象方法)袖外。
如果一個(gè)接口被注解@FunctionalInterface修飾史隆,則該接口只能有一個(gè)抽象方法,否則會(huì)報(bào)錯(cuò)曼验。
四.引用類的構(gòu)造器及方法
在Lambda中若是直接調(diào)用了一個(gè)方法泌射,且調(diào)用方法的形參和要實(shí)現(xiàn)的接口抽象方法形參一致粘姜,則可以進(jìn)一步簡(jiǎn)寫(xiě)。舉例如下:
接口定義:
@FunctionalInterface
interface Converter<F,T> {
T convert(F form);
}
Lambda表達(dá)式
//舊的寫(xiě)法
Converter<String,Integer> convert1 = (from) -> Integer.valueOf(from);
//新的寫(xiě)法
Converter<String,Integer> convert2 = Integer::valueOf;
System.out.println(convert1.convert("1234"));
引用其他類型的方法
- 例子中的方法是Integer類的靜態(tài)方法熔酷,如果是某個(gè)類的實(shí)例方法孤紧,則應(yīng)該使用一個(gè)對(duì)象加::來(lái)引用。如拒秘,obj::func;
- 如果要調(diào)用的一個(gè)構(gòu)造方法(抽象方法返回的是一個(gè)對(duì)象)号显,則應(yīng)該這樣使用:Integer::new。用new代替構(gòu)造方法名字躺酒。
五.Lambda訪問(wèn)外部變量及接口默認(rèn)方法
訪問(wèn)局部變量
- 可以訪問(wèn)局部的final變量押蚤,但不能修改。
- 與匿名內(nèi)部類不同的是阴颖,外部變量不需要顯示地聲明為final活喊,但卻要有final的特點(diǎn),不能被修改量愧,在Lambda之后被修改也不行钾菊。
訪問(wèn)成員變量和靜態(tài)變量
可以任意讀寫(xiě),舉例如下:
public class FunctionInterface {
String str = "init";
static Integer num = 0;
public void testScope() {
Converter<String,Integer> convert = (from) -> {
//在這里可訪問(wèn)成員變量和靜態(tài)變量
str = from;
num = Integer.valueOf(from);
return num;
};
System.out.println(convert.convert("3453"));
System.out.println(convert.convert("5678"));
}
}
訪問(wèn)接口的默認(rèn)方法
在匿名類中可以訪問(wèn)接口定義的默認(rèn)方法偎肃,在Lambda中不可以訪問(wèn)煞烫。
六.內(nèi)置函數(shù)式接口
Java8中內(nèi)置了許多函數(shù)式接口,包括Comparator和Runnable等累颂,它們被添加了@FunctionalInterface注解滞详,以用來(lái)支持Lambda表達(dá)式。
6.1Predicate斷言
查看源碼紊馏,這個(gè)函數(shù)式接口中要實(shí)現(xiàn)的方法為:boolean test(T t); 即一個(gè)判斷傳入值真假的方法料饥,當(dāng)然判斷的規(guī)則由你自己定義。
如定義一個(gè)判斷字符串長(zhǎng)度是否大于10的Predicate:
import java.util.function.Predicate;
public class PredicateDemo {
public static void main(String[] args) {
Predicate<String> predicate = s -> s.length() > 10;
System.out.println(predicate.test("hello"));
System.out.println(predicate.test("hello,world!"));
}
}
6.2 Function
查其源碼朱监,需要實(shí)現(xiàn)一個(gè)R apply(T t)的方法岸啡。這個(gè)接口提供鏈?zhǔn)秸{(diào)用、組合的功能赫编。
6.3 Supplier
Supplier<Person> personSupplier = Person::new;
personSupplier.get();
6.4 Consumer
??
Consumer<Person> greeter = (p) -> System.out.println("Hello, " + p.firstName);
greeter.accept(new Person("Luke", "Skywalker"));
6.5 Optional
七.Stream流
什么是Stream流巡蘸?
參考資料
Stream流提供了一種對(duì)集合Collection的方便的操作。分為“中間操作”和“終端操作”兩種擂送。中間操作的結(jié)果還是返回一個(gè)Stream悦荒,可以繼續(xù)操作;而終端操作會(huì)返回一個(gè)結(jié)構(gòu)不能繼續(xù)流操作了嘹吨。
要使用Sream首先要通過(guò)Collection的Stream方法獲取一個(gè)Stream對(duì)象搬味。
7.1 Filter過(guò)濾 中間操作
篩選出集合中滿足一定條件的元素。Stream有一個(gè)filter方法,入?yún)⑹且粋€(gè)Predicate身腻,篩選結(jié)果是Predicate.test為true的集合的Stream产还。下面來(lái)看一個(gè)篩選出String集合中以"s"開(kāi)頭的String的程序:
List<String> list = Arrays.asList("hello","world","apple","people","sea",
"watch","table","book","school","help");
list
.stream() //獲取stream
.filter(s -> s.startsWith("s")) //設(shè)置filter。中間操作
.forEach(System.out::println); //打印出流中的元素嘀趟。終端操作
7.2 Sorted排序 中間操作
可以給sorted()方法傳入一個(gè)Comparator用來(lái)自定義排序脐区,否則將使用默認(rèn)排序規(guī)則。
list
.stream()
.sorted((a,b) -> b.compareTo(a))
.forEach(System.out::println);
7.3 Map
map方法入?yún)橐粋€(gè)Function函數(shù)式接口她按。調(diào)用map方法將對(duì)集合中的每一個(gè)元素執(zhí)行一下Function中的apply方法牛隅,并返回由其返回值組成的集合的流。
舉例:將表示數(shù)字的字符串集合全部轉(zhuǎn)換為數(shù)字再加一后輸出酌泰。
List<String> list = Arrays.asList("10","100","1000");
System.out.println("---- test map ----");
list
.stream()
.map(Integer::valueOf) //轉(zhuǎn)換為整數(shù)
.map(a -> a + 1) //執(zhí)行加一操作
.forEach(System.out::println);
7.4Match匹配
是種終端操作媒佣,結(jié)果不是stream對(duì)象,而是boolean值
根據(jù)Predicate指定的規(guī)則判斷集合中是否有匹配的陵刹,有的話返回true默伍。
有三種形式,anyMatch:有一個(gè)匹配就返回true衰琐。allMatch:全部匹配返回true也糊。noneMatch:全部不匹配返回true。
boolean anyMatch(Predicate<? super T> predicate);
boolean allMatch(Predicate<? super T> predicate);
boolean noneMatch(Predicate<? super T> predicate);
7.5 Count計(jì)數(shù)
終端操作羡宙,統(tǒng)計(jì)stream中元素的個(gè)數(shù)狸剃。
long count =
list
.stream() //獲取stream
.filter(s -> s.startsWith("s")) //設(shè)置filter。中間操作
.count();
7.6 Reduce
list[0]和list[1]執(zhí)行操作狗热,得到的結(jié)果為result钞馁。result再和list[2]執(zhí)行操作,得到的結(jié)果result匿刮。依次進(jìn)行僧凰,對(duì)所有元素執(zhí)行一遍。
根據(jù)上述描述也可以看出熟丸,這里的“操作”必須滿足兩個(gè)入?yún)⒀荡搿⒎祷刂凳峭活愋偷摹?/p>
reduce方法的入?yún)⑹牵?strong>BinaryOperator<T> 這里的T就是集合的元素類型。
舉例:求Integer集合中所有元素的和虑啤。
List<Integer> list = Arrays.asList(1,2,3,4,5);
System.out.println("---- test reduce ----");
Optional<Integer> sum =
list
.stream()
.reduce((a,b) -> a + b);
sum.ifPresent(System.out::println);
注意:reduce的返回值為Optional<T>。