美文网首页
Android 第一行代码(3版) - kotlin - Lam

Android 第一行代码(3版) - kotlin - Lam

作者: 刚刚8888 | 来源:发表于2023-02-18 11:06 被阅读0次

由来:
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原理

相关文章

网友评论

      本文标题:Android 第一行代码(3版) - kotlin - Lam

      本文链接:https://www.haomeiwen.com/subject/fclvkdtx.html