JAVA8學習筆記-Lambda
阿新 • • 發佈:2019-01-01
JAVA8學習筆記-Lambda
宣告:本文僅為學習記錄,不保證絕對的準確性
1.一次關於蘋果的改良
先定義一個蘋果的實體類
public class Apple {
private String color;
private long weight;
//省略了建構函式,getter和setter,toString
}
版本一
這是一段篩選出綠色蘋果的程式碼,缺點很明顯,擴充套件性很差。
public static List<Apple> findGreenApple(List<Apple> apples) {
List<Apple> list = new ArrayList<>();
for (Apple apple : apples){
if("green".equals(apple.getColor())){
list.add(apple);
}
}
return list;
}
版本二
版本二就是在版本一的基礎上,加了一個入參,顏色。這樣可以進一步選擇要篩選的顏色,但仍舊很侷限。
版本三
準備工作
public interface AppleFilter {
boolean filter(Apple apple);
}
public static List<Apple> findApple(List<Apple> apples, AppleFilter appleFilter){
List<Apple> list = new ArrayList<>();
for (Apple apple : apples){
if(appleFilter.filter(apple)){
list.add(apple);
}
}
return list;
}
//實現類
public static class GreenFilter implements AppleFilter{
@Override
public boolean filter(Apple apple) {
return (apple.getColor().equals("green"));
}
}
版本三定義了一個過濾介面,通過不停的增加這個介面的實現,可以滿足各種各樣的需求。但是隨著需求不停的增加,會多出很多幾乎只使用一次的實現類,這樣就會顯得不優雅。(大佬們都愛拿優雅說事情,跟個風不會捱打吧)
//新定義一個蘋果類的list
List<Apple> list = Arrays.asList(new Apple("green",120),
new Apple("red",130),new Apple("green",140));
//傳入新定義的實現類
List<Apple> apples = findApple(list, new GreenFilter());
版本四
版本四採用了匿名內部類的方法,但是java8實戰的作者說,這樣程式碼還是很多,而且可讀性不好。(心裡有一丟丟的認同)
List<Apple> apples = findApple(list, new AppleFilter() {
@Override
public boolean filter(Apple apple) {
return apple.getColor().equals("red");
}
});
System.out.println(apples);
版本五
這個就是我們的lambda表示式了,是不是感覺和匿名內部類很像,其實本來就很像,底層實現也很像。但是,程式碼變少了對不對,對不對!(其實我覺得還行,但是還是向大佬們看齊,絕不向不優雅的程式碼做出妥協)
List<Apple> apples = findApple(list, apple -> {
return apple.getColor().equals("green");
});
System.out.println(apples);
匿名內部類背鍋系列
這段程式碼是作者為了diss匿名內部類的易讀性差的特點貼出來的。
可以看出,核心就在於方法裡的this代指的是誰,我們可以看出,this代表runnable類,因此結果是5。
public class MeaningOfThis
{
public final int value = 4;
public void doIt()
{
int value = 6;
Runnable r = new Runnable(){
public final int value = 5;
public void run(){
int value = 10;
System.out.println(this.value);
}
};
r.run();
}
public static void main(String...args)
{
MeaningOfThis m = new MeaningOfThis();
m.doIt();
}
}
[email protected]
這個介面要求類有且只有一個方法,包括繼承過來的方法(default方法除外);如果沒有標註的話,也可以用lambda表示式(在滿足上面要求的情況下)。jdk1.8在很多方法中都加上了這個介面,例如runnable(),comparator()。
runnable()的演示。
//匿名內部類
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(Thread.currentThread().getName());
}
}).start();
//lambda表示式
new Thread(() -> System.out.println(Thread.currentThread().getName())).start();
這段程式的結果是
Thread-0
Thread-1
Comparator()的演示。
//匿名內部類
Comparator<Apple> byColor = new Comparator<Apple>() {
@Override
public int compare(Apple o1, Apple o2) {
return o1.getColor().compareTo(o2.getColor());
}
};
//lambda表示式
Comparator<Apple> byColor2 = (o1, o2) -> o1.getColor().compareTo(o2.getColor());
//list的sort實現,但沒有單獨定義comparator
list.sort((a1, a2) -> a1.getColor().compareTo(a2.getColor()));
3.Lambda語法和函式式介面
語法
先總結一下。
(parameters) -> expression
(parameters) -> { statements; }
舉例子。
() -> {} // public void run()
() -> "haha" // public String run()
() -> {return "haha";} // public String run()
Predicate
predicate的作用像是篩選用的,或者說做判斷。
//Predicate<T> 傳入T
private static List<Apple> filter(List<Apple> source, Predicate<Apple> predicate) {
List<Apple> result = new ArrayList<>();
for (Apple a : source) {
if (predicate.test(a))
result.add(a);
}
return result;
}
//執行,篩選出綠色蘋果
List<Apple> result = filter(list, (apple) -> apple.getColor().equals("green"));
//LongPredicate 傳入一個Long型的值
private static List<Apple> filterByLongPredicate(List<Apple> source,
LongPredicate predicate) {
List<Apple> result = new ArrayList<>();
for (Apple a : source) {
if (predicate.test(a.getWeight()))
result.add(a);
}
return result;
}
//執行,篩選出中重量大於100的蘋果
List<Apple> result = filterByLongPredicate(list, w -> w > 100);
//BiPredicate 傳入兩個引數
private static List<Apple> filterByBiPredicate(List<Apple> source,
BiPredicate<String, Long> predicate) {
List<Apple> result = new ArrayList<>();
for (Apple a : source) {
if (predicate.test(a.getColor(), a.getWeight()))
result.add(a);
}
return result;
}
//執行,選出綠色和大於100克的蘋果
List<Apple> result = filterByBiPredicate(list, (s, w) -> s.equals("green") && w > 100);
Consumer
consumer像是拿過來用掉了,沒有判斷,更沒有返回值,單純自己做一些處理。
//接收T,此處為apple
private static void simpleTestConsumer(List<Apple> source, Consumer<Apple> consumer) {
for (Apple a : source) {
consumer.accept(a);
}
}
//執行,列印傳進來的apple
simpleTestConsumer(list, a -> System.out.println(a));
//接收兩個T,一個String,一個apple
private static void simpleBiConsumer(String c, List<Apple> source, BiConsumer<Apple, String> consumer) {
for (Apple a : source) {
consumer.accept(a, c);
}
}
//執行,列印傳進去的string和apple
simpleBiConsumer("XXX", list, (a, s) -> System.out.println(s + a);
Function
返回什麼值,自己定義,最後一個引數為返回值。
//返回值為String,傳入apple
private static String testFunction(Apple apple, Function<Apple, String> fun) {
return fun.apply(apple);
}
//執行,只是做了個toString處理
String result = testFunction(new Apple("yellow", 100), (a) -> a.toString());
//返回值為apple,傳入string和long
private static Apple testBiFunction(String color, long weight, BiFunction<String, Long, Apple> fun) {
return fun.apply(color, weight);
}
//執行,新建apple物件
Apple a = testBiFunction("Blue", 130, (s, w) -> new Apple(s, w));
Supplier
//沒有引數,只有返回值,此處定義為apple
private static Apple createApple(Supplier<Apple> supplier) {
return supplier.get();
}
//執行,初始化一個apple物件
Apple a2 = createApple(() -> new Apple("Green", 100));
//等價於下面,不過下面的是string物件
//TODO,不太懂
Supplier<String> s = String::new;
String abc = s.get();//abc是空字串
Runnable中用到的變數預設為final
int i = 0;
Runnable r = new Runnable() {
@Override
public void run() {
System.out.println(i);//此處的i會報錯,因為需要i為final型,下面的i++破壞了這個條件
}
};
i++;
4.函式推導
靜態方法
//靜態方法,這段程式碼的作用為把字串轉為數字,Function的兩個引數,String為入參,Integer為返回值,parseInt則為Integer的靜態方法
Function<String, Integer> f = Integer::parseInt;
Integer result = f.apply("123");
System.out.println(result);
類方法
//類方法,這段程式碼的作用為找出字串中特定位置的字元,BiFunction的String和Integer為入參,含義分別為要處理的字串和要取出字元的位置,Character則為返回值型別,顯然呼叫的String類的成員方法,因為雙引號前為類名,而不是例項,所以直接傳字串即可
BiFunction<String, Integer, Character> f2 = String::charAt;
Character c = f2.apply("hello", 2);
System.out.println(c);
例項方法
//例項方法,作用於上面的一致,只不過不用傳字串,因為呼叫的方法本身就是這個例項的方法。
String string = new String("hello");
Function<Integer, Character> f3= string::charAt;
Character c2 = f3.apply(4);
System.out.println(c2);