Android 第一行代码(3版) - kotlin - Lambda表达式

由来:
Lambda 表达式(lambda expression)是一个匿名函数,Lambda表达式基于数学中的「λ演算」得名,直接对应于其中的lambda抽象(lambda abstraction)。
λ这个符号读lambda,所以匿名函数又被称为lambda函数,最早lambda函数应该是出现Lisp中的,因为它是一门「纯函数式编程的语言」。
λ演算(英语:lambda calculus,λ-calculus)是一套从数学逻辑中发展,以变量绑定和替换的规则,来研究函数如何抽象化定义、函数如何被应用以及递归的形式系统。

它是java从函数式编程语言借鉴,方便开发人员编写清晰、高效代码的一种编程风格。
java8中从函数式编程引入了两个核心思想:方法(方法引用)和lambda表达式可以作为值传递,并且函数或者方法可以有效、安全地并行执行。

Lambda 在编程语言中往往是一个匿名函数,也就是说Lambda 是一个抽象概念,而编程语言提供了配套支持,比如在 Java 中其实为Lambda 进行配套的就是函数式接口,通过函数式接口生成匿名类和方法进行Lambda 式的处理。

栗子:

// 苹果
public class Apple {
    private String color;
    // Apple类中行为参数化
    public interface ApplePredicate {
        boolean filter(Apple apple);
    }
    // Apple类中用来筛选属性的方法
     public static List<Apple> filter(List<Apple> apples, ApplePredicate predicate) {
            List<Apple> filterList = new ArrayList<>();
            for (Apple apple : apples) {
                if (predicate.filter(apple)) {
                    filterList.add(apple);
                }
            }
            return filterList;
     }
}

List<Apple> apples = new ArrayList<Apple>();
...
// => 传统遍历
// => 筛选出列表中颜色为绿色的苹果
 List<Apple> greenApples = new ArrayList<>();
 for (Apple apple : apples) {
    if ("green".equals(apple.getColor())) {
            greenApples.add(apple);
         }
  }

// => 3种实现方式
// 1. ApplePredicate实现类
 public class GreenApplePredicate implements ApplePredicate {
        @Override
        public boolean filter(Apple apple) {
            return "green".equals(apple.getColor());
        }
 }
List<Apple> f1 = Apple.filter(apples, new GreenApplePredicate());
// 2. 匿名内部类
List<Apple> f2 = Apple.filter(apples, new ApplePredicate() {
        @Override
        public boolean filter(Apple apple) {
            return "green".equals(apple.getColor());
        }
 });
// 3.  lambda表达式
List<Apple> f3 = Apple.filter(apples
                      , (Apple apple) -> "green".equals(apple.getColor()));

lambda函数优缺点

  • 优点:
    代码简洁,开发迅速
    函数式编程
  • 缺点:
    不建议在lambda函数内写太复杂的条件语句,不易读;
    lambda函数与普通函数相比,不会提高程序运行效率的提高,很多计算未必有传统的 for 性能要高
    不容易进行调试

Java中使用

// 1. 不需要参数,返回值为 5  
() -> 5  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s) 

栗子:
// 无参无返回值
TestA testA = () -> System.out.print("无参无返回值\n");
testA.exec();
// 无参有返回值
TestB testB = () -> "无参有返回值\n";
System.out.print(testB.exec());
// 有参无返回值
TestBB testBB = (String s) -> System.out.print(s);
testBB.exec("有参无返回值\n");
// 有参有返回值
TestC testC = (String a, String b) -> a + b;
System.out.print(testC.exec("2个参数", "有返回值\n"));
// 不可移至尾部
textD(1, 2, (int x, int y) -> x + y);
// 局部变量截获
NumAdd a = add();
System.out.print(a.exec(10) + "\n");
System.out.print(a.exec(20) + "\n");

int total = 0;
private NumAdd add() {
    //int total = 0;
    //可以捕获局部变量,但不能对局部变量修改,默认是final类型
    return (int num) -> {
        total += num;
        return total;
    };}
private void textD(int p1, int p2, Fn fn) { 
    System.out.print(fn.exec(p1, p2) + "\n");}
public interface TestA { void exec(); }
public interface TestB { String exec(); }
public interface TestBB { void exec(String s); }
public interface TestC { String exec(String a, String b); }
public interface Fn { int exec(int a, int b); }
public interface NumAdd { int exec(int num); }

kotlin中使用

//表示无参数无返回值的Lambda表达式类型
{() -> Unit}
//表示接收一个T类型参数,无返回值的Lambda表达式类型
{(T) -> Unit}
//表示接收一个T类型参数,返回一个R类型值的Lambda表达式类型
{(T) -> R}
//表示接收一个T类型和P类型的参数,返回一个R类型值的Lambda表达式类型
{(T, P) -> R}
//表示接收一个T类型参数和一个接收P、Q类型两个参数并返回一个S类型的值的Lambda表达式类型参数
//,返回一个R类型值的Lambda表达式类型
{(T, (P,Q) -> S) -> R}

栗子:
// 无参无返回值
val testA = { print("无参无返回值\n") }
testA()
// 无参有返回值
val testB = { "无参有返回值\n" }
print(testB())
// 有参无返回值
val testBB: (String) -> Unit = { print(it) }
testBB("有参无返回值\n")
// 有参有返回值
val testC = { strA: String, strB: String 
              -> strA + strB }
print(testC("2个参数", "有返回值\n"))
// 最后一个参数是lambda表达式, 可移至尾部
textD(1, 2) { x: Int, y: Int -> x + y}
// 局部变量截获
val a = add()
print(a(10).toString() + "\n")
print(a(20).toString() + "\n")

fun add() : (Int) -> Int {
    var total = 0
    return { num: Int -> total += num
        total
    }}
fun textD(p1: Int, p2:Int ,fn: (Int, Int) -> Int) {
    print(fn(p1, p2).toString() + "\n")}

规则:
java:

  • 参数类型可以省略,如果需要省略,每个参数的类型都要省略。
  • 参数的小括号里面只有一个参数,那么小括号可以省略
  • 如果方法体当中只有一句代码,那么大括号可以省略
  • 如果方法体中只有一条语句,其是return语句,那么大括号可以省略,且去掉return关键字。

kotlin:

  • lambda表达式是总是由花括号{}包裹

  • lambda表达式声明的函数参数列表在 -> 前面,与普通函数参数声明方式一样,但:
    * 参数列表不能用括号()包裹
    * 没有任何参数时,则不需要括号和->
    * 只有一个参数时,则可以省略参数使用默认的it代替,同时省略->

  • lambda表达式声明的函数体在 -> 后面

  • lambda表达式声明的函数的返回值不是Unit,则最后一个表达式作为函数的返回值

不同语言中的lambda

// =>Python
lambda [arg1[,arg2,arg3....argN]]:expression
// 栗子:
add2 = lambda x,y:x+y 
print add2(1,2)     #3
sum2 = lambda x,y=10:x+y
print sum2(1)       #11 
print sum2(1,100)   #101

// =>C++
[ capture clause ] (parameters) -> return-type  {  definition of method  }
// 栗子:
void func(std::vector<int>& v) {  
     std::for_each(v.begin(), v.end(), [](int i) {  
            cout << i << endl; 
     });
}

// => Javascript
(p1 [,p2,p3,....pn]) => { code block  }
// 栗子:
let func  = x => x * x;
func(2) #4

// => OC  - Block 类似lambda
^返回值 参数列表 表达式
// 栗子:
void (^blockTest)(void) = ^{
    NSLog(@"block");
};
blockTest();
// 带参
^(int a){
     NSLog(@"block的使用");
};
// 不带参
^{
     NSLog(@"最简捷的block的使用");
};
// block作为参数
void funcWithParm(void (^block)(int parm))
{
    block(10);
}

// swift 闭包
// 无参无返回值
let testA = { print("无参无返回值闭包") }
testA()
// 无参有返回值
let testB = { return "无参有返回值闭包" }
print(testB())
// 有参有返回值
let testC = {(strA: String, strB: String) 
              in return strA + strB }
print(testC("2个参数", "有返回值闭包"))
// 尾随闭包
textD(p1: 1, p2: 2) { 
   return $0 + $1    // #3 
} 
func textD(p1: Int, p2:Int ,fn: (Int, Int) -> Int) {
    print(fn(p1, p2))
}
// 闭包可能捕获非常量变量
let a = add();
print(a(10))        // # 10
print(a(20))        // # 30
func add() -> (Int) -> Int {
    var total = 0
    return {num in
        total += num;
        return total;
    }
}

函数式接口

  • @FunctionalInterface注解
    有且只有一个抽象方法的接口被称为函数式接口,Java 8中专门为函数式接口引入了一个新的注解:@FunctionalInterface 。该注解可用于一个接口的定义上,一旦使用该注解来定义接口,编译器将会强制检查该接口是否确实有且仅有一个抽象方法(equal和hashcode方法不算),否则将会报错。
    但是这个注解不是必须的,只要符合函数式接口的定义,那么这个接口就是函数式接口。
// forEach 参数类型
@FunctionalInterface 
public interface Consumer<T> {  
   void accept(T t);
}
  • Consumer: 消费性接口
    Consumer通过名字可以看出它是一个消费函数式接口,主要针对的是消费(1..n 入参, 无返回)这个场景,典型的例子是 forEach 方法
public interface Iterable<T> {
  ...
   default void forEach(Consumer<? super T> action) {
        Objects.requireNonNull(action);
        for (T t : this) {
            action.accept(t);
        }
    }
}

// 栗子:
List<Integer> list = Arrays.asList(1,2,3,4);
list.forEach(item -> {
     System.out.print(item);
});
// =>
list.forEach(System.out::println);
  • Supplier: 供给型接口
    Supplier通过名字比较难看出来它是一个场景的函数式接口,它主要针对的是说获取(无入参,有返回)这个场景,典型的例子是 Stream 中的 collect 方法,通过自定义传入我们想要取得的某种对象进行对象收集
// collect 参数类型
@FunctionalInterface
public interface Supplier<T> {
    T get();
}
public static <T>
    Collector<T, ?, List<T>> toList() {
        return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
                                   (left, right) -> { left.addAll(right); return left; },
                                   CH_ID);
 }
public interface Stream<T> extends BaseStream<T, Stream<T>> {
    ...
    <R, A> R collect(Collector<? super T, A, R> collector);
}
// 栗子:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> newNumbers = numbers.stream()
            .filter(x -> x >= 2)
            .collect(Collectors.toList()); // 将大于等于2的数重新收集成一个集合
  • Function: 函数型接口
    Function 接口的名字不太能轻易看出来它的场景,它主要针对的则是 转换(有入参,有返回,其中T是入参,R是返回)这个场景,典型的例子是Stream 中 map 系列方法和 reduce 系列方法
// map 参数类型
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
    ...
}
public interface Stream<T> extends BaseStream<T, Stream<T>> {
    ...
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    T reduce(T identity, BinaryOperator<T> accumulator);
}
// 栗子:
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
List<Integer> doubleNumbers = numbers.stream()
                .map(number -> number * 2)
                .collect(Collectors.toList());

List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
Integer sum = numbers.stream()
          .reduce(0, (a, b) -> a + b);

// reduce 参数类型
@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
    ...
}
  • Predicate: 断言型接口
    Predicate主要针对的是判断(有入参,有返回,凡是返回的类型固定为Boolean。可以说Function 是包含Predicate的 )这个场景,典型的例子是Stream 中 filter方法
// filter参数类型
@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
    ...
}
public interface Stream<T> extends BaseStream<T, Stream<T>> {
    ...
    Stream<T> filter(Predicate<? super T> predicate);
}

Stream表达式
Stream,就是JDK8又依托于函数式编程特性为集合类库做的一个类库,它其实就是jdk提供的函数式接口的最佳实践。它能让我们通过lambda表达式更简明扼要的以流水线的方式去处理集合内的数据,可以很轻松的完成诸如:过滤、分组、收集、归约这类操作。

其中Stream的操作大致分为两类

  • 中间型操作


  • 终结型操作


    2fa4e35bb804c120402d8385defd0a1c.png

Lambda原理

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,287评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,346评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,277评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,132评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,147评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,106评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,019评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,862评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,301评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,521评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,682评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,405评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,996评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,651评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,803评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,674评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,563评论 2 352

推荐阅读更多精彩内容