Lambda的小入门

Java8新特性(基础知识)

本文是参杂着个人见解的博客笔记,但总体上是依据 <<Java8实战>>这本书的大纲来进行编写,示例也是大多依据其中.

 

为什么要学习Java8的新特性

Java8版本是Java更新迭代中非常大的一个版本,他改变了许多Java编写的风格,也使许多冗余的程序,变得更为简便和易懂,让我们来看下两端代码的不同,

首先我们有一个需求: 按照重量从小到大排序苹果

Java8之前的代码

        
        Collections.sort(inventory, new Comparator<Apple>() {
            @Override
            public int compare(Apple o1, Apple o2) {
                //compareTo  >  返回值为1  调换位置
                return o1.getWeight()- o2.getWeight();
            }
        });
    

在java8之前的代码我们需要多写几行,并且一眼见过去并不能直接明了的理解代码要表达的意思,我们看看下面的代码

java8后的实现代码

    //   需要implements Comparable接口
       inventory.sort(Comparator.comparing(Apple::getWeight));

可以直接看出这个inventory,想要去排序,并且是通过Apple中的重量去排序.

说个老实话你可能不习惯,因为我开始也是,但是后面你会爱上这种形式的.

首先我们看下有什么不同呢 我们用到了一个Java8中的新接口Comparator,和方法引用Apple::getWeight,这两者我们在后面都会讲解到

 

Lambda表达式

上面的例子中,我们其实是运用了Lambda表达式,当然,它也不只只有以上的功能,那只是冰山一脚~

那么我们继续看下,这些更新Java的大佬到底想要我们尽量使用lambda到底有什么要注意和方便的

传递代码

这个的思想核心是将代码作为函数传入到方法中,它可以更加灵活的应对不同的要求,不信我们可以看下以下的苹果示例

需求:我要筛选出绿色的苹果

 public static List<Apple> filterGreenApples(List<Apple>inventory){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if("green".equals(apple.getColor())){
                result.add(apple);
            }
        }
        return result;
    }

看下以上代码,确实没啥问题,但是,问题是我换需求了,我现在要大于150g的苹果,然后你就复制粘贴得到以下代码

需求:大于150g的苹果

public static List<Apple> filterHeavyApples(List<Apple>inventory){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if(apple.getWeight() > 150){
                result.add(apple);
            }
        }
        return result;
    }

这两段代码其实只有 if(....) 这一行是不同的,也违背了我们不应该要有重复代码的心愿,并且需要复制粘贴~

 

那接下来我们看下Java8后使用Lambda的写法

   public static boolean isGreenApple(Apple apple){
        return "green".equals(apple.getColor());
    }
    public static boolean isHeavyApple(Apple apple){
        return apple.getWeight() > 150;
    }
    
    //Predicate接口,是一个函数式接口,接受一个函数作为参数,返回一个boolean值,后面会有源码解析
    //若要使用可以filterApples(inventory, Apple::isGreenApple);
    static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if(p.test(apple)){
                result.add(apple);
            }
        }
        return result;
    }

这样直接看上去,是感觉跟以前的写法不同的,不过是将方法抽取出来的一种感觉,但是别着急,我们慢慢来改进下.

但是 我们也应该知道代码数是减少了,也更加明亮了,我们看下面改进下

传递方法到Lambda

filterApples(inventory, (Apple a) -> "green".equals(a.getColor()));
        
filterApples(inventory, (Apple a) -> a.getWeight() > 150);

有人会想,这箭头是啥玩意,但若你学过javascript你应该更容易理解,我们现在可以在参数中写我们想要筛选苹果的需求,但这是不够的.

 

现在我们从上面的对比中是可以发现lambda的写法确实比以前好的多,可是优秀不了很多,所以新的需求来了

应对多变的需求

筛选绿苹果

public static List<Apple> filterGreenApples(List<Apple>inventory){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if("green".equals(apple.getColor())){
                result.add(apple);
            }
        }
        return result;
    }

由上,绿色被固定了不能变化颜色需求,需要改来改去,我们以前手段可以尝试将其抽象化,用参数color传入方法中

把颜色作为参数

public static List<Apple> filterApplesByColor(List<Apple>inventory, String color){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if(apple.getColor().equals(color)){
                result.add(apple);
            }
        }
        return result;
    }

这种就把颜色变为参数传入方法,现在可以应对一些颜色不同的苹果筛选了

将重量最为参数

public static List<Apple> filterApplesByWeight(List<Apple>inventory, int weight){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if(apple.getWeight() > weight){
                result.add(apple);
            }
        }
        return result;
    }

 

这样一看,妈耶怎么日常DRY(don't repeat yourself)

现在我们在来狠点的,要多点需求同时进行的

需求:重量大于150,颜色要绿色,可能你会使用以下代码

public static List<Apple> filterApplesByWeightAndColor(List<Apple>inventory, int weight, String color){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if(apple.getWeight() > weight && apple.getColor().equals(color)){
                result.add(apple);
            }
        }
        return result;
    }

if(...)这一行语句又重复了,并且在类中这次又多个方法,变复杂了,虽然两个需求能应付,那么多上十来个需求呢,这并不是不可能的!

所以我们使用这种方法会相对简单点

 public static boolean isGreenApple(Apple apple){
        return "green".equals(apple.getColor());
    }
    public static boolean isHeavyApple(Apple apple){
        return apple.getWeight() > 150;
    }
    
    //Predicate接口,是一个函数式接口,接受一个函数作为参数,返回一个boolean值,后面会有源码解析
    //若要使用可以filterApples(inventory, Apple::isGreenApple);
    static List<Apple> filterApples(List<Apple> inventory, Predicate<Apple> p){
        List<Apple> result = new ArrayList<>();
        for(Apple apple : inventory){
            if(p.test(apple)){
                result.add(apple);
            }
        }
        return result;
    }

但这其实也是在需求多时也是特别复杂的,所以我们要解决这些啰嗦的东东

 

现在我们可以使用匿名类来去解决这一类的问题

首先我们需要一个接口

public interface ApplePredicate {
    boolean test (Apple apple);
}

在测试类中

     List<Apple> redApples = filterApples(inventory, new ApplePredicate(){
​
            @Override
            public boolean test(Apple apple) {
                return "red".equals(apple.getColor());
            }
        });
        List<Apple> WeightApples = filterApples(inventory, new ApplePredicate(){
​
            @Override
            public boolean test(Apple apple) {
                return apple.getWeight() > 150;
            }
        });
​
​
 static List<Apple> filterApples(List<Apple> inventory, ApplePredicate p) {
        List<Apple> result = new ArrayList<>();
        for (Apple apple : inventory) {
            if (p.test(apple)) {
                result.add(apple);
            }
        }
        return result;
    }

这样会让人舒服很多,重复代码也变少了,但是匿名类笨重,因为需要很多的空间,并且我个人有时对匿名内部类用起来挺费解的

 

现在我们用Lambda来尝试

首先也需要一个接口

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
    }

在测试类中

public class test {
    public static void main(String[] args) {
        //inventory 库存量
        List<Apple> inventory = new ArrayList<Apple>();
        Apple apple2 = new Apple(100, "green");
        Apple apple1 = new Apple(200, "red");
        Apple apple3 = new Apple(150, "green");
        Apple apple4 = new Apple(170, "pink");
        inventory.add(apple2);
        inventory.add(apple1);
        inventory.add(apple3);
        inventory.add(apple4);
​
​
        List<Apple> redApples = filterApples(inventory,(Apple a) -> "red".equals(a.getColor()));
        List<Apple> WeightApples = filterApples(inventory,(Apple a) -> a.getWeight() > 150);
​
​
        System.out.println(redApples);
        System.out.println(WeightApples);
    }
    static  <T>List<T> filterApples(List<T> inventory, Predicate<T> p) {
        List<T> result = new ArrayList<>();
        for (T t : inventory) {
            if (p.test(t)) {
                result.add(t);
            }
        }
        return result;
    }
 }

看这比以前的代码简洁了很多不是? 提供了一个泛型的方法,也可以运用到其他方法, 灵活而且简洁!

再来个helloworld例子

/*Thread d = new Thread(new Runnable(){
    public void run (){
        System.out.println("hello,world");
        
}});*/
​
//变成如下形式
        Thread d = new Thread(() -> {
            System.out.println("hello,world");
        });

 

那Lambda的形式到底该如何书写呢

(Apple a) -> "green".equals(a.getColor())
由上可知lambda表达式由三部分组成
参数    箭头    主体
​
这里要注意得是主体, 当主题只有一行时, 可以省略return, 若是主题被{}所包裹,主题必须两行以上,或者有return语句,以下是几个例子
​
(String s) -> s.length();//省略了return语句,检测返回值为int
(Apple a) -> a.getWeight() > 150  //省略了return语句,检测返回值为boolean
(int x, int y) -> {
    System.out.println("result:");
    System.out.println(x+y);
}          //两个参数没有返回值

更多得例子可以在网上搜查

 

Lambda在哪使用呢

lambda使用得条件,必须是有函数式接口得地方,什么叫函数式接口呢,例如前面提起得Predicate<T>

@FunctionalInterface
public interface Predicate<T> {
​
    boolean test(T t);
}

通过查看源码每个函数式接口只能有并且有一个唯一得抽象方法,只有如此,才可与我们写得函数联系在一起,那让我们再看一下@FunctionalInterface标签

/**
 * An informative annotation type used to indicate that an interface
 * type declaration is intended to be a <i>functional interface</i> as
 * defined by the Java Language Specification.
 *
 * Conceptually,
 
 a functional interface has exactly one abstract method.
 函数式接口只有一个抽象方法
 
 Since {@linkplain java.lang.reflect.Method#isDefault()
 * default methods} have an implementation, they are not abstract.  If
 * an interface declares an abstract method overriding one of the
 * public methods of {@code java.lang.Object}, that also does
 * <em>not</em> count toward the interface's abstract method count
 * since any implementation of the interface will have an
 * implementation from {@code java.lang.Object} or elsewhere.
 *
 
 * <p>Note that instances of functional interfaces can be created with
 * lambda expressions, method references, or constructor references.
 函数式接口可被lambda表达式,方法引用,构造函数引用使用
 
 *
 * <p>If a type is annotated with this annotation type, compilers are
 * required to generate an error message unless:
 *
 * <ul>
 * <li> The type is an interface type and not an annotation type, enum, or class.
 * <li> The annotated type satisfies the requirements of a functional interface.
 * </ul>
 *
 * <p>However, the compiler will treat any interface meeting the
 * definition of a functional interface as a functional interface
 * regardless of whether or not a {@code FunctionalInterface}
 * annotation is present on the interface declaration.
 *
 * @jls 4.3.2. The Class Object
 * @jls 9.8 Functional Interfaces
 * @jls 9.4.3 Interface Method Body
 * @since 1.8   //java8开始
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface FunctionalInterface {}

通过以上源码我们可知道函数式接口得用法

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值