高階函數和Java的Lambda

在數學和計算機科學中,高階函數是至少滿足下列一個條件的函數:

  • 接受一個或多個函數作為輸入

  • 輸出一個函數

奇人透码4肖八码:java世界迎來新的一等公民——函數

香港彩票透码 www.kptln.icu java 8引入了函數式編程。函數式編程重點在函數,函數變成了Java世界里的一等公民,函數和其他值一樣,可以到處被定義,可以作為參數傳入另一個函數,也可以作為函數的返回值,返回給調用者。利用這些特性,可以靈活組合已有函數形成新的函數,可以在更高層次上對問題進行抽象。

使用高階函數之前的求和、求平方和、求立方和的寫法:

public class TestHighOrderFunction {   public static int identity(int x) {      return x;
   }   public static int sum_integers(int a, int b) {     int sum = 0;     for (int i = a; i <= b; i++) {
       sum += identity(i);
     }    return sum;
   }   public static int square(int x) {      return x * x;
   }  public static int sum_square(int a, int b) {     int sum = 0;     for (int i = a; i <= b; i++) {
        sum += square(i);
     }     return sum;
   }   public static double cube(int x) {      return x * x * x;
   }   public static int sum_cubes(int a, int b) {      int sum = 0;      for (int i = a; i <= b; i++) {
         sum += cube(i);
      }      return sum;
   }    public static void main(String[] a) {

        System.out.println(sum_integers(1, 10)); // return 55
        System.out.println(sum_square(1, 10));  // return 385
        System.out.println(sum_cubes(1, 10));  // return 3025
    }
}

我們發現sum_開頭的方法里,代碼很類似,三者唯一區別在于

   sum += identity(i);
   sum += square(i);
   sum += cube(i);

在軟件工程里有DRY(don't repeat yourself )的準則。我們來看看使用高階函數怎樣優化剛才的這些代碼:

 public interface Function {     int opera(int a);
 } public static void main(String[] a) {

    Function identity = x->x;
    Function square = x->x*x;
    Function cube = x -> x*x*x;
    System.out.println(sum(identity, 1,10)); // return 55
    System.out.println(sum(square, 1,10)); // return 385
    System.out.println(sum(cube, 1,10));   // return 3025
 } public static int sum(Function term, int a, int b) {     int sum = 0;     for (int i = a; i <= b; i++) {
        sum += term.opera(i);
     }     return sum;
 }

得到的結果,跟上面的TestHighOrderFunction類中運行的結果是一樣的。不過,這里的sum方法中使用了

   sum += term.opera(i);

取代了原先的代碼。term.opera(i)對應的是原先identity(i)、square(i)、cube(i),在這里Function函數被當做參數進行傳遞。這就是高階函數的特性。

對于for循環,我們還能用更優雅的方式進行優化,下面使用了遞歸的方式。

 public interface Function {     int opera(int a);
 } public static void main(String[] a) {

      Function identity = x->x;
      Function square = x->x*x;
      Function cube = x -> x*x*x;
      Function inc = x->x+1; // 定義next函數
      System.out.println(sum(identity, 1,inc,10)); // return 55
      System.out.println(sum(square, 1,inc,10)); // return 385
      System.out.println(sum(cube, 1,inc,10));   // return 3025
 } public static int sum(Function term, int a, Function next, int b) {      if (a>b) {          return 0;
      } else {          return term.opera(a) + sum(term, next.opera(a), next, b);
      }
 }

@FunctionalInterface

在java 8之前我們使用Thread,可能是這樣的

   new Thread(new Runnable() {    
        public void run() {        
              System.out.println("test");    
       }
   }).start();

由于Java 8引入了lambda表達式,我們可以這樣寫

   new Thread(()->System.out.println("test"));

lambda表達式源于lambda演算。

Lambda演算可以被稱為最小的通用程序設計語言。它包括一條變換規則(變量替換)和一條函數定義方式,Lambda演算之通用在于,任何一個可計算函數都能用這種形式來表達和求值。因而,它是等價于圖靈機的。盡管如此,Lambda演算強調的是變換規則的運用,而非實現它們的具體機器??梢勻銜饈且恢指詠砑怯布姆絞?。

我們點擊Runnable的源碼時,發現Runnable使用了@FunctionalInterface,這在java 8之前是沒有的。

@FunctionalInterfacepublic interface Runnable {    /**
     * When an object implementing interface <code>Runnable</code> is used
     * to create a thread, starting the thread causes the object's
     * <code>run</code> method to be called in that separately executing
     * thread.
     * <p>
     * The general contract of the method <code>run</code> is that it may
     * take any action whatsoever.
     *
     * @see     java.lang.Thread#run()
     */
    public abstract void run();
}

@FunctionalInterface是Java 8為函數式接口引入的一個新的注解。表明該接口是函數式接口,它只包含唯一一個抽象方法。任何可以接受一個函數式接口實例的地方,都可以用lambda表達式。

我們再來看一個匿名函數的例子。

button.setOnClickListener(new Button.OnClickListener(){   
     public void onClick(View v) {        
         Log.i(TAG,"點擊button");    
     }
});

我們將匿名函數改成lambda表達式

button.setOnClickListener((v)-> Log.i(TAG,,"點擊button"));

這樣改造的好處在于,lambda對象的創建是通過字節碼指令invokedynamic來完成的,減少了類型和實例的創建消耗。而匿名類需要新的對象的創建。

JDK中的函數式接口舉例

java.lang.Runnable,
java.awt.event.ActionListener,
java.util.Comparator,
java.util.concurrent.Callable
java.util.function包下的接口,如Consumer、Predicate、Supplier等

簡化的lambda——方法引用(Method Reference)

lambda已經簡化了代碼的寫法,然而方法引用進一步簡化了lambda的寫法。
方法引用的使用方式:類名::方法名

類型使用方式備注
引用靜態方法ContainingClass::staticMethodNameInteger::valueOf簡化了i->Integer.valueOf(i)的寫法
引用特定對象的實例方法containingObject::instanceMethodNames::toString()簡化了()->s.toString()
引用特定類型的任意對象的實例方法ContainingType::methodNameSystem.out::println簡化了(s)->System.out.println(s),其中System.out表示的是PrintStream對象
引用構造函數ClassName::newString::new簡化了()->new String()

我們來看一個簡單的例子,對User按照name來進行排序,最初我們會這樣寫。

  User u1 = new User("tony");
  User u2 = new User("cafei");
  User u3 = new User("aaron");

  List<User> users = Arrays.asList(u1,u2,u3);

  Collections.sort(users, new Comparator<User>(){   @Override
   public int compare(User u1, User u2) {    return u1.getName().compareTo(u2.getName());
   }

  });

在java 8以后,Comparator增加了一個靜態方法comparing(Function<? super T, ? extends U> keyExtractor),我們可以把排序的寫法簡化成這樣:

Collections.sort(users, Comparator.comparing((User u)->u.getName()));

如果使用方法引用,還可以更加簡化代碼

Collections.sort(users,Comparator.comparing(User::getName));

集合中的應用

在java 8中可以使用新增的api Streams來操作集合,Streams是區別于java.io包里的InputStream 和 OutputStream的概念,是對集合功能的增強。如果你曾經了解過Scala、RxJava等函數式編程,那么看了它的語法以后一定會覺得似曾相識。我們來看兩段代碼,看看它是如何使用的。

List<Integer> list = Arrays.asList(1, 2, 3, 5, 7, 9, 10)
    .stream()
    .filter(i -> i >= 5)
    .collect(Collectors.toList());

System.out.println("list=" + list); // return list=[5, 7, 9, 10]
  Arrays.asList("tony", "cafei", "aaron")
           .stream()
           .map(str -> str.toUpperCase())
           .forEach(it -> System.out.println(it));

上面的代碼還可以使用方法引用的方式:

  Arrays.asList("tony", "cafei", "aaron")
        .stream()
        .map(String::toUpperCase)
        .forEach(System.out::println);

使用這樣的鏈式調用非常cool。而且,map、filter等方法都是高階函數。

寫在最后

lambda是java 8最為重要的特性,lambda表達式并非只是Java的語法糖,而是由編譯器和JVM共同配合來實現的。自從使用了lambda以后我感覺再也回不去了。

來源:簡書-fengzhizi715

上一篇: 甲骨文七年如一日死磕谷歌,再上訴安卓對 Java 侵權

下一篇: Java8并發教程:Threads和Executors

分享到: 更多
pk10走势图杀码技巧 pk10六码两期全天计划 双色球近300期结果 快乐时时计划软件 双色球全部组合数据 一分快3大小 三星组选包胆什么意思 幸运飞艇6码7期倍投 我玩龙虎输了100万 捕鱼达人 北京pk赛车合法么 双色球杀红绝招超准 六码三期倍投 网球比分 平刷王时时彩计划软件 pk拾永久免费计划软件