java8的函數(shù)式編程

Lambda表達式

1.初步印象

示例代碼:

View view = findViewById(R.id.textView2);
view.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Log.i("lambda", String.format("點擊[%d]", v.getId()));
                Toast.makeText(MainActivity.this, "顯示文字", Toast.LENGTH_SHORT).show();
            }
        });

這是我們經(jīng)常設置監(jiān)聽器的代碼匕积,不管你要做的事情是什么项乒,必須有:

new View.OnClickListener(){
  @Override
  public void onClick(View view){
    ...
  }
}

這代碼就很繁瑣,很多“雜音”九榔,而且當你需要使用外部的Context時候,你還不得不老是調用 XXXX.this涡相,Lambda表達式不會從超類(supertype)中繼承任何變量名哲泊,也不會引入一個新的作用域

如果用Lambda表達式簡化,代碼如下:

  view.setOnClickListener(v -> {
    Log.i("lambda", String.format("點擊[%d]", v.getId()));
    Toast.makeText(this, "顯示文字", Toast.LENGTH_SHORT).show();
  });

Lambda表達式可以認為是對行為的抽象催蝗,表達式比較清晰的表明了一個我們要做什么

2.Lambda表達式常用形式

示例代碼:

    // 沒有參數(shù)的方法切威,也沒有返回
    Runnable run = () -> System.out.println("do something");
    // 1個參數(shù)的方法可以省略(),且有返回, 只有一句, 默認認為是要返回表達式的結果
    IntUnaryOperator func = x -> x + 1; // 輸入x, 返回 (x + 1)的結果
    // 2個參數(shù)的方法
    DoubleBinaryOperator func2 = (x, y) -> x + y;
    // 可以在()里面定義好變量的類型丙号,多數(shù)情況下先朦,可以不需要,由編譯器自己推斷參數(shù)類型
    IntBinaryOperator func3 = (int x, int y) -> x + y;
    // 多行語句犬缨,需要用{表達},且需要返回值的函數(shù)不能省略return
    IntBinaryOperator func4 = (x, y) -> {
        int temp = x % 3;
        int temp2 = y / 2;
        return temp + temp2;
    };

Lambda表達式是由左右兩部和"->"構成的:
1.左邊部分代表參數(shù)喳魏,無參用()表達,一個參數(shù)可以省略()怀薛,多個參數(shù)必須用()包含刺彩。
2.右邊部分代表方法體,可以是一個表達式,也可以是{}包含的代碼塊

Lambda表達式對外部局部變量的引用屬于值引用创倔,類似匿名類引用外部局部變量時候嗡害,必須聲明為final,只不過Lambda表達式引用時候畦攘,不需要顯示的聲明為final霸妹,減少代碼雜音。

3.Java8對函數(shù)的抽象---函數(shù)接口

我們經(jīng)常使用只有一個方法的接口來表示某特定方法行為知押,比如我們的OnClickListener叹螟。這種函數(shù)接口反復出現(xiàn),現(xiàn)在Java8提供了一些核心的函數(shù)接口台盯,抽象化了這些行為罢绽。(在java.util.function包下面),列出比較重要的幾個函數(shù)接口如下:

1.Predicate<T>
代表:參數(shù)為T -> Boolean爷恳,用于判斷
示例 :

Predicate<Integer> func = x -> x > 1;
System.out.println("是否大于1? " + func.test(2));

2.Consumer<T>
代表:參數(shù) T ->{} 有缆,用于處理某事物
示例:

Consumer<Integer> func = x -> System.out.println(x);
func.accept(2);

3.Function<T, R>
代表 : 參數(shù) T -> R, 用于對數(shù)據(jù)進行處理變換温亲,返回R類型
示例:

Function<Point, Double> func = p -> p.distance(0, 0);
System.out.println("點[3,4]距離原點的距離是? " + func.apply(new Point(3, 4)));

4.Supplier<T>
代表:參數(shù) ( ) -> T棚壁,生成對象
示例:

Supplier<Point> func = () -> new Point(3, 4);

5.UnaryOperator<T>
代表:參數(shù) T -> T,對T進行處理栈虚,并且返回同類型
示例:

UnaryOperator<Integer> addSelf = x -> x + 1;
System.out.println("1+1="+addSelf.apply(1));

6.BinaryOperator<T>
代表:參數(shù)(T, T) -> T 袖外,變換返回同類型T
示例:

BinaryOperator<Integer> max = (x, y) -> {
    return x > y ? x : y;
};
System.out.println("比較5和4,大的是: " + max.apply(4, 5));

Java8 流

Java8 對集合類庫進行了大量修改魂务,并且引入了新概念:流

1.外部迭代到內(nèi)部迭代
/**
 * 計算字符串里面所有數(shù)字的和, 只考慮單一字符
 * 
 * @param text
 * @return
 */
private static void countNum(String text) {
    char[] arr = text.toCharArray();
    int sum = 0;
    for (int i = 0; i < arr.length; i++) {
        if (Character.isDigit(arr[i])) {
            sum += Character.digit(arr[i], 10);
        }
    }
    System.out.println(String.format("它們的和是[%d]", sum));
}

需要進行for循環(huán)曼验,每次迭代都必須寫類似代碼,如果多個for循環(huán)粘姜,還要考慮很多其他問題鬓照,如果要并行處理,就需要修改for循環(huán)邏輯
傳統(tǒng)的for循環(huán)孤紧,或者Iterator叫做外部迭代豺裆。Java 8提供了流,進行內(nèi)部迭代号显,代碼如下:

/**
 * 計算字符串里面所有數(shù)字的和
 * 
 * @param text
 * @return
 */
private static void countNumSeq(String text) {
    IntStream stream = IntStream.range(0, text.length())
        .mapToObj(i -> text.charAt(i))//拿到每個字符
        .filter(c -> Character.isDigit(c))//過濾掉不是數(shù)字的字符
        .mapToInt(c -> Character.digit(c, 10));//將所有字符轉為數(shù)字
    System.out.println(String.format("它們的和是[%d]", stream.sum()));
}

看起來貌似代碼復雜多了臭猜,但是我們清晰的看出了所有要執(zhí)行的“意圖”:
1.查找所有字符
2.過濾掉不是數(shù)字的
3.將字符轉為數(shù)字
4.求和
而for循環(huán)代碼無法如此清晰的表達我們需要執(zhí)行的動作,所以代碼得簡潔性得到提高押蚤。

惰性求值方法:在stream方法里面蔑歌,像mapToObj這樣的方法,只刻畫了什么樣的stream而不會去產(chǎn)生新集合揽碘,結果也只是返回一個新的stream
及早求值方法:像count這樣會得到一個具體值次屠,會對集合真正進行操作

所以上面代碼园匹,只有真正執(zhí)行到count(及早求值方法)時候,才會去對集合操作帅矗,不會進行多次循環(huán)偎肃。

2.常用流操作

可以發(fā)現(xiàn)煞烫,流基本全部都是傳入前面提到的函數(shù)接口

1.Stream<T> filter(Predicate<? super T> predicate);
代表:對流進行過濾
2.<R> Stream<R> map(Function<? super T, ? extends R> mapper);
代表:對流進行轉換從T->R
類似:
IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
3.<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
代表:將流轉換為新類型的流,而map是得到新類型
示例如下:

/**
 * 合并流
 */
List<Integer> together = Stream.of(asList(1, 2), asList(3, 4))
.flatMap(numbers -> numbers.stream())
.collect(toList());
assertEquals(asList(1, 2, 3, 4), together);

4.Stream<T> distinct();
代表:去重,依賴T.equals()方法判斷
5.Stream<T> sorted();
代表:排序涛舍,依賴T是可比較的拂共,如果沒有實現(xiàn)Comparable會報錯
6.Stream<T> sorted(Comparator<? super T> comparator);
代表:排序
7.Stream<T> peek(Consumer<? super T> action);
代表:對流進行處理的時候,可以對這些被處理的元素進行消費料饥,而不影響新集合內(nèi)容

List<String> list = Stream.of("one", "two", "three", "four","five", "six", "seven")
        .filter(e -> e.length() > 3)
        .peek(e -> System.out.println("Filtered value: " + e))
        .collect(Collectors.toList());
System.out.println("List size " + list.size());

輸出結果:
Filtered value: three
Filtered value: four
Filtered value: five
Filtered value: seven
List size 4

8.Stream<T> limit(long maxSize);
代表:最多拿取多少個源數(shù)據(jù)
上面代碼加入:

stream.limit(2)

Filtered value: three
Filtered value: four
List size 2

9.void forEach(Consumer<? super T> action);
代表:循環(huán)遍歷
10.Stream<T> skip(long n);
代表:對開始的n個數(shù)據(jù)忽略
上面代碼加入:

stream.skip(1);

Filtered value: four
Filtered value: five
Filtered value: seven
List size 3

11.long count();
代表:獲取新集合個數(shù)

12.boolean anyMatch(Predicate<? super T> predicate);
13.boolean allMatch(Predicate<? super T> predicate);
14.T reduce(T identity, BinaryOperator<T> accumulator);
代表:reduce 操作可以實現(xiàn)從一組值中生成一個值蒲犬,如 count方法,max方法岸啡,min方法原叮,都可以用reduce來實現(xiàn)。

書本練習題: 只用 reduce 和 Lambda 表達式實現(xiàn) Stream 上的map()

試著寫了下巡蘸,不知道是不是這個意思奋隶,如下:

/**
 * reduce模擬map操作
 */
private static <T, R> List<R> map(Stream<T> stream, Function<T, R> todo) {
    List<R> list = new ArrayList<>();
    stream.reduce(null, (init, e) -> {
        list.add(todo.apply(e));
        return init;
    });
    return list;
}

List<Double> list = map(Stream.of("1", "2", "3", "4"),
                (s) -> Double.valueOf(s));
list.stream()
    .forEach((d) -> System.out.println(d));

Java8 新增特性

1.接口靜態(tài)方法

上面代碼,我們在使用stream時候悦荒,可以如下調用:

Stream<String> s = Stream.of("1", "2", "3", "4");

查看源碼唯欣,發(fā)現(xiàn)Stream是接口,如下:

    @SafeVarargs
    @SuppressWarnings("varargs") // Creating a stream from an array is safe
    public static<T> Stream<T> of(T... values) {
        return Arrays.stream(values);
    }

也就是說可以直接在接口里面定義我們自己的靜態(tài)方法實現(xiàn)搬味。

如果一個方法有充分的語義原因和某個概念相關境氢,那么就應該將該方法和相關的類或接口放在一起,而不是放到另一個工具類中碰纬。這有助于更好地組織代碼

2.接口default方法

解決場景(二進制接口的兼容性):

1.Java8中萍聊,對Collection 接口中增加了新的 stream()。
2.如果你在Java8 以前實現(xiàn)了自己的MyList繼承了Collection接口悦析,那么你在Java8運行時候會報錯寿桨,因為以前的實現(xiàn)里面根本沒有stream()
3.為解決如上問題,Java8新增default字段她按,標記默認實現(xiàn)方法Collection 接口告訴它所有的子類:“如果你沒有實現(xiàn) stream 方法牛隅,就使用我的吧∽锰”接口中這樣的方法叫作默認方法

public interface IStuff {
    String getName();
    double getPrice();

    default void showSelf() {
        System.out.println(String.format("[%s]%.02f", getName(), getPrice()));
    }
}

//子類的實現(xiàn)不一定要有showSelf媒佣,如果沒有回默認使用default方法
IStuff stuff = new IStuff() {

    @Override
    public String getName() {
        // TODO Auto-generated method stub
        return "iPhone7";
    }

    @Override
    public double getPrice() {
        // TODO Auto-generated method stub
        return 4897.4647;
    }
};
stuff.showSelf();
3.方法引用

Lambda 表達式有一個常見的用法:Lambda 表達式經(jīng)常調用參數(shù),比如:

Function<Integer, String[]> func = (i) -> new String[i];
Function<Point, Double> func2 = p -> p.getX();

這種用法如此普遍陵刹,Java 8 提供了一個簡寫語法默伍,叫作方法引用

func = String[] :: new;
func2 = Point :: getX;

形式就是** Classname :: Methodname** ,如果想調用構造函數(shù)則用new代替

參考:

《Java 8 函數(shù)式編程》

最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市也糊,隨后出現(xiàn)的幾起案子炼蹦,更是在濱河造成了極大的恐慌,老刑警劉巖狸剃,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件掐隐,死亡現(xiàn)場離奇詭異,居然都是意外死亡钞馁,警方通過查閱死者的電腦和手機虑省,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來僧凰,“玉大人探颈,你說我怎么就攤上這事⊙荡耄” “怎么了伪节?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵,是天一觀的道長绩鸣。 經(jīng)常有香客問我怀大,道長,這世上最難降的妖魔是什么全闷? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任叉寂,我火速辦了婚禮,結果婚禮上总珠,老公的妹妹穿的比我還像新娘屏鳍。我一直安慰自己,他們只是感情好局服,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布钓瞭。 她就那樣靜靜地躺著,像睡著了一般淫奔。 火紅的嫁衣襯著肌膚如雪山涡。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天唆迁,我揣著相機與錄音鸭丛,去河邊找鬼。 笑死唐责,一個胖子當著我的面吹牛鳞溉,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播鼠哥,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼熟菲,長吁一口氣:“原來是場噩夢啊……” “哼看政!你這毒婦竟也來了?” 一聲冷哼從身側響起抄罕,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤允蚣,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后呆贿,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嚷兔,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年榨崩,在試婚紗的時候發(fā)現(xiàn)自己被綠了谴垫。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片章母。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡母蛛,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出乳怎,到底是詐尸還是另有隱情彩郊,我是刑警寧澤,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布蚪缀,位于F島的核電站秫逝,受9級特大地震影響,放射性物質發(fā)生泄漏询枚。R本人自食惡果不足惜违帆,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望金蜀。 院中可真熱鬧刷后,春花似錦、人聲如沸渊抄。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽护桦。三九已至含衔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間二庵,已是汗流浹背贪染。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留催享,地道東北人杭隙。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓,卻偏偏與公主長得像睡陪,于是被迫代替她去往敵國和親寺渗。 傳聞我的和親對象是個殘疾皇子匿情,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容

  • Java8 in action 沒有共享的可變數(shù)據(jù),將方法和函數(shù)即代碼傳遞給其他方法的能力就是我們平常所說的函數(shù)式...
    鐵牛很鐵閱讀 1,235評論 1 2
  • 原文http://www.codeceo.com/article/learn-java-lambda.html L...
    與我常在1053閱讀 1,135評論 1 7
  • 簡介 概念 Lambda 表達式可以理解為簡潔地表示可傳遞的匿名函數(shù)的一種方式:它沒有名稱信殊,但它有參數(shù)列表炬称、函數(shù)主...
    劉滌生閱讀 3,207評論 5 18
  • 注:之前關于Java8的認知一直停留在知道有哪些修改和新的API上,對Lambda的認識也是僅僅限于對匿名內(nèi)部類的...
    mualex閱讀 2,826評論 1 4
  • 原文鏈接: Lambdas 原文作者: shekhargulati 譯者: leege100 lambda表達式是...
    忽來閱讀 6,599評論 8 129