Java-05 匿名類、Lambda、方法引用

匿名類(Anonymous Class)

  • 接口、抽象類的實現類,在整個項目中只使用過一次,可以考慮使用匿名類
// Person per = new Person();
    // per.run();// Person Run

    // 如果一個類只在一個地方使用、 可以使用匿名類
    Runable person = new Runable() {
      @Override
      public void run() {
        System.out.println("Person run");
      }
    };

    person.run();

    // 如果需要初始化之后立即執(zhí)行某個函數 可以
    new Runable() {
      @Override
      public void run() {
        System.out.println("Person run");
      }
    }.run();

匿名類的使用注意

匿名類跟局部類很相似

  • 匿名類不能定義除了編譯時常量之外的static成員
  • 匿名類只能訪問final或者有效final的局部變量
  • 匿名類可以直接訪問外部類的所有成員(包括private成員)
  • 匿名類只有在實例相關的代碼塊中才能直接訪問外部類的實例成員(實例變量、實例方法)
  • 匿名類不能自定義構造方法,但可以有初始化塊

匿名類的常見用途

代碼傳遞
public interface MyBlock {
  void execute();
}

public class Times {
  // 計算代碼耗時
  public static void test(MyBlock block){
    long begin = System.currentTimeMillis();
    block.execute();
    long end = System.currentTimeMillis();

    double duration = (end - begin) / 1000.0;
    System.out.println("耗時:" + duration + "s");
  }
}

   Times.test(new MyBlock(){
      @Override
      public void execute() {
        int age = 10;
        for (int i = 0; i < age; i++) {
          System.out.println(i);
        }
      }
    });


回調

類似于OC中的回調

public interface NetBlock {
  void success(Object response);
  void failure();
}

public class Network {
  public static void get(String url, NetBlock callBack){
    // 模仿一個url請求
    boolean res = true;
    if (res) {
      Object resp = null;
      callBack.success(resp);
    } else {
      callBack.failure();
    }
  }
}

Network.get("https://xxxxx", new NetBlock(){
      @Override
      public void success(Object response) {
        System.out.println("請求成功");
      }

      @Override
        public void failure() {
          System.out.println("請求失敗");
        }
    });
過濾器

匿名函數可以實現類似于filter的功能

Lambda

Lambda表達式是Java 8 開始有的語法

函數式接口(Functional Interface):只包含一個抽象方法的接口

@FunctionalInterface // 表示這是一個函數式接口
public interface Testable {
  void test();
}
  • 當匿名類實現的是函數式接口時,可以使用lambda來簡化
    (參數列表) -> {
      reture xxx;
    } 
// 普通寫法
    Times.test(new MyBlock(){
      @Override
      public void execute() {
        int age = 10;
        for (int i = 0; i < age; i++) {
          System.out.println(i);
        }
      }
    });

    // lambda表達式
    
    Times.test(() -> {
      int age = 10;
        for (int i = 0; i < age; i++) {
          System.out.println(i);
        }
    });


Supplier的使用

有時使用Supplier傳參,可以避免代碼的浪費執(zhí)行(有必要時再執(zhí)行代碼)

  public static void main(String[] args) {
    getFirstNotEmptyString("jack", makeString());
  }
  static String makeString() {
    return String.format("%d %d %d", 1, 2, 3);
  }
  static String getFirstNotEmptyString(String s1, String s2) {
    if (s1 != null || s1.length() != 0) return s1;
    if (s2 != null || s2.length() != 0) return s2;
    return null;
  }

上面的示例中,我們確定了第一個參數不為空,也就沒必要執(zhí)行第二個參數的代碼,但是現在makeString方法仍然會被調用。

可以考慮使用Supplier這個函數式接口

  public static void main(String[] args) {
    getFirstNotEmptyString("jack", makeString());
    getFirstNotEmptyString("jack", new Supplier<String>() {
      @Override
      public String get() {
        return makeString();
      }
    });
    // lambda簡化
    getFirstNotEmptyString("jack", () -> makeString());
  }
  static String makeString() {
    return String.format("%d %d %d", 1, 2, 3);
  }
  // 第二個參數的代碼會執(zhí)行
  static String getFirstNotEmptyString(String s1, String s2) {
    if (s1 != null || s1.length() != 0) return s1;
    if (s2 != null || s2.length() != 0) return s2;
    return null;
  }

  static String getFirstNotEmptyString(String s1, Supplier<String> s2Supplier ) {
    if (s1 != null || s1.length() != 0) return s1;
    String s2 = s2Supplier.get();
    if (s2 != null || s2.length() != 0) return s2;
    return null;
  }

Lambda的使用注意

  • lambda只能訪問final或者有效final的局部變量
  • Lambda沒有引入新的作用域
@FunctionalInterface
public interface Testable {
  void test(int v);
}

public class OuterClass {
  private int age = 1;
  public class InnerClass {
    private int age = 2;
    void innerTest() {
      Testable t = v -> {
        System.out.println(v);  // 3
        System.out.println(age); // 2
        System.out.println(this.age); // 2
        System.out.println(InnerClass.this.age); // 2
        System.out.println(OuterClass.this.age); // 1
      };

      // 由于Lambda沒有引入新的作用域 所以表達式里的代碼有些類似于直接寫在表達式的外面
      
      /* 類似于把代碼寫在這里
        System.out.println(v);  // 3
        System.out.println(age); // 2
        System.out.println(this.age); // 2
        System.out.println(InnerClass.this.age); // 2
        System.out.println(OuterClass.this.age); // 1
        */
      
      t.test(3);
    }
  }

  public static void main(String[] args) {
    new OuterClass().new InnerClass().innerTest();
  }
}

方法引用(Method Reference)

如果Lambda中的內容僅僅是調用某個方法,可以使用方法引用

種類 用法
引用靜態(tài)方法 ClassName::staticMethodName
引用特定對象的實例方法 ObjectName::instanceMethodName
引用特定類型的任意對象的實例方法 ClassName::methodName
引用構造方法 ClassName::new
引用當前類中定義的實例方法 this::instanceMethodName
引用父類中定義的實例方法 super::instanceMethodName

方法引用本質是一個語法糖,其本質還是那么復雜

public interface Testable {
 int test(int v1, int v2);
}

public class Main {
 public static void main(String[] args) {
   // lambda表達式
   Testable t1 = (v1, v2) -> {
     return Math.max(v1, v2);
   };
   // 稍微精簡下
   Testable t2 = (v1, v2)-> Math.max(v1, v2);

   // 使用方法引用簡化
   Testable t3 = Math::max;

   System.out.println(t1.test(30, 50)); // 50
   System.out.println(t2.test(30, 50)); // 50
   System.out.println(t3.test(30, 50)); // 50

 }
}

引用特定對象的實例方法

public interface Testable {
 void test(int v);
}
public class Person {
 public void setAge(int age) {
   System.out.println("Person - age = " + age);
 }
}



public class Main {
 static void execute(Testable t, int v) {
   t.test(v);
 }
 public static void main(String[] args) {
   // 
   execute(v -> new Person().setAge(v),10);
   // 
   execute(new Person()::setAge, 10);
 }

}

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

友情鏈接更多精彩內容