JAVA8新特性(一)

JAVA8

行为参数化

概念

行为参数化:让方法接受多种行为(或战略)作为参数,并在内部使用,来完成不同的行为。

这样描述不太好理解,换一种方式描述:通过判断方法的参数实现不同的操作,比如简单工厂模式,通过传入的参数经过条件语句判断创建什么对象;面向接口编程,根据参数传递的具体实现类对象调用不同的实现方法。

在java8中不仅仅可以传递基本数据类型、对象,也可以传递代码块、函数。


函数式接口

函数式接口:接口interface中只有一个抽象方法(抽象方法省略了public和abstract),此接口上有注解@FunctionalInterface,但是可以此接口可以有default默认方法。如: Comparator<T>、Runnable、Callable<T>

函数描述符: 函数式接口的抽象方法的签名称,比如Comparator<T>接口有一个方法compare(),此方法有两个参数o1,o2,返回值类型int,那么此方法的函数描述符就是(T,T)->Integer。

java API还有几个函数式接口Predicate、Consumer、Function

Predicate

java.util.function.Predicate接口定义了一个test抽象方法,接受泛型T对象,返回boolean。
它的函数描述符就是 (T)->boolean。

示例:

/**
*函数式接口
*/
@FunctionalInterface
public interface Predicate<T>{
    boolean test(T t);
}

/**
*自定义一个方法,参数一个是集合,另外一个是Predicate
*逻辑:如果集合中的对象不为空,那么就将其存储到results集合中,最终返回results。
*/
public static <T> List<T> filter(List<T> list, Predicate<T> p) {
    List<T> results = new ArrayList<>();
    for(T s: list){
        if(p.test(s)){
            results.add(s);
        }
    }
    return results;
}

/**
*测试调用方式
*/
List<String> nonEmpty = filter(listOfStrings, (String s) -> !s.isEmpty());

Consumer

java.util.function.Consumer定义了一个accept的抽象方法,它接受泛型T的对象,没有返回( 返回类型void)。你如果需要访问类型T的对象,并对其执行某些操作,就可以使用这个接口。它的函数描述符就是 (T)->{}

示例:

/**
*函数式接口
*/
@FunctionalInterface
public interface Consumer<T>{
    void accept(T t);
}

/**
* 自定义一个方法,一个参数为集合list,另一个参数为Consumer
* 逻辑:循环执行Consummer的accept方法,即根据传递的实现输出
*/
public static <T> void forEach(List<T> list, Consumer<T> c){
    for(T i: list){
        c.accept(i);
    }
}

/**
*测试调用方式
*/
forEach(Arrays.asList(1,2,3,4,5),(Integer i) -> System.out.println(i));

Function

java.util.function.Function<T, R>接口定义了一个叫作apply的方法,它接受一个
泛型T的对象,并返回一个泛型R的对象。它的函数描述符就是(T)->R

示例:

/**
*函数式接口
*/
@FunctionalInterface
public interface Function<T, R>{
    R apply(T t);
}

/**
* 自定义一个方法,一个参数是集合list,另一个参数Function
* 逻辑:循环集合,根据传递的Function函数式调用apply执行,并且将结果存储到result中,然后返回
*/
public static <T, R> List<R> map(List<T> list,Function<T, R> f) {
    List<R> result = new ArrayList<>();
    for(T s: list){
        result.add(f.apply(s));
    }
    return result;
}

/**
*测试调用方式
*/
List<Integer> l = map(Arrays.asList("lambdas","in","action"),(String s) ->s.length());

总结了Java API中提供的最常用的函数式接口及其函数描述符

函数式接口函数描述符
PredicateT->boolean
ConsumerT->void
Function<T,R>T->R
Supplier()->T
UnaryOperatorT->T
BinaryOperator(T,T)->T
BiPredicate<L,R>(L,R)->boolean
BiConsumer<T,U>(T,U)->void
BiFunction<T,U,R>(T,U)->R

实例如下:

自定义类中的方法,用于过滤苹果:
    public List<Apple> fileterApples(List<Apple> appleList, Predicate<Apple> predicate){
        List<Apple> result = new ArrayList<>();
        for(Apple apple:appleList){
            if (predicate.test(apple)){
                result.add(apple);
            }
        }
        return result;
    }

两种测试方式:
    /**
     * 根据不同条件编写方法,传递方法
     */
    private static void test1() {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple("green", 120));
        apples.add(new Apple("red", 160));
        apples.add(new Apple("yellow", 100));
        LamdbaDemo lamdbaDemo = new LamdbaDemo();
        List<Apple> appleList = lamdbaDemo.fileterApples(apples, lamdbaDemo::filterAppleByWeight);
        System.out.println(appleList.toString());
    }

    /**
     * 如果传递的方式只使用一次,那么可以直接传递代码
     */
    private static void test2() {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple("green", 120));
        apples.add(new Apple("red", 160));
        apples.add(new Apple("yellow", 100));
        LamdbaDemo lamdbaDemo = new LamdbaDemo();
        List<Apple> appleList = lamdbaDemo.fileterApples(apples, (Apple a) -> StringUtils.equals("green", a.getColor()));
        System.out.println(appleList.toString());
    }
    
    private static boolean filterAppleByWeight(Apple apple){
        return 150 < apple.getWeight();
    }
    

Lamdba表达式

相关名称

谓词:一个返回boolean值的函数,java8中类Predicate<T>,并且提供了test()方法

匿名类:不需要提前创建实现类,程序执行的时候在方法参数中直接new接口,并且实现接口方法

构建Lamdba

在这里插入图片描述
参数列表:调用方法的参数
箭 头:箭头把参数列表和lamdba主体分隔
主 体:Lamdba的返回值

有效lamdba表达式

(1) () -> {}                                 
(2) () -> "Raoul"
(3) () -> {return "Mario";}
(4) (String s) -> {return "Mario" + s;}

解释
(1)没有参数,返回值为void,主体为空的方法
(2)没有参数,返回值为String,方法主体 return "Raoul";
(3)没有参数,返回值为String,方法主体 return "Mario"; 因为return是控制语句需要加{}
(4)参数为String s,返回值为String,方法主体 return "Mario" + s; 因为return是控制语句需要加{}

无效Lamdba表达式

(1) (Integer i) -> return "Alan" + i;
(2) (String s) -> {"IronMan";}

解释
(1)参数为Integer i,返回值为String,主体方法因为有控制语句return,所以需要加{}
(2)参数为String s,返回值为String,方法主体应该去掉; ,或者写成{return "IronMan";}

在这里插入图片描述

使用场景

I. 函数式接口实现通过Lamdba表达式替代匿名实现类

    /**
     * 匿名内部类实现排序
     */
    private static void test() {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple("green", 120));
        apples.add(new Apple("red", 160));
        apples.add(new Apple("yellow", 100));
        apples.sort(new Comparator<Apple>() {
            @Override
            public int compare(Apple o1, Apple o2) {
                return o2.getWeight() - o1.getWeight();
            }
        });
        System.out.println(apples.toString());
    }

    /**
     * Lamdba实现排序
     */
    private static void test() {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple("green", 120));
        apples.add(new Apple("red", 160));
        apples.add(new Apple("yellow", 100));
        apples.sort((o1, o2) -> o2.getWeight() - o1.getWeight());
        System.out.println(apples.toString());
    }

II. 函数描述符即函数式接口的抽象方法的签名基本上就是Lamdba表达式的签名
示例:

(1) execute(() -> {});
    public void execute(Runnable r){
        r.run();
    }
(2) public Callable<String> fetch() {
        return () -> "Tricky example ;-)";
    }
(3) Predicate<Apple> p = (Apple a) -> a.getWeight();

答案:只有1和2是有效的。
第一个例子有效,是因为Lambda() -> {}具有签名() -> void,这和Runnable中的
抽象方法run的签名相匹配。请注意,此代码运行后什么都不会做,因为Lambda是空的!

第二个例子也是有效的。事实上, fetch方法的返回类型是Callable<String>。
Callable<String>基本上就定义了一个方法,签名是() -> String,其中T被String代替
了。因为Lambda() -> "Trickyexample;-)"的签名是() -> String,所以在这个上下文
中可以使用Lambda。

第三个例子无效,因为Lambda表达式(Apple a) -> a.getWeight()的签名是(Apple) ->
Integer,这和Predicate<Apple>:(Apple) -> boolean中定义的test方法的签名不同。

III. 环绕执行模式即资源处理时一个常见的模式就是打开一个资源,做一些处理,然后关闭资源,这个设置和清理阶段总是很类似,并且换围绕着执行处理的那些重要代码。这就是所谓的环绕执行模式。比如前面的示例中,筛选苹果的条件不同,但是筛选前后的操作相同,那么就可以将筛选的条件作为参数进行传递,但是必须要有一个函数式接口的方法与筛选调节的描述一致。

筛选苹果常规方式
1.按照苹果颜色为绿色

    public List<Apple> fileterApples(List<Apple> appleList){
        List<Apple> result = new ArrayList<>();
        for(Apple apple:appleList){
            if (StringUtils.equals("green",apple.getColor())){
                result.add(apple);
            }
        }
        return result;
    }

2.按照苹果体重大于15克

    public List<Apple> fileterApples(List<Apple> appleList){
        List<Apple> result = new ArrayList<>();
        for(Apple apple:appleList){
            if (15 < apple.getWeight()){
                result.add(apple);
            }
        }
        return result;
    }

两段代码只是条件语句不同,此条件用lamdba表达式描述 (Apple)->boolean

那么自定义一个函数式接口,其方法描述满足(Apple)->boolean(当然此函数式接口其实就是Predicate)

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

此时比较方法应该写成

    public List<Apple> fileterApples(List<Apple> appleList,LamdbaApple lamdbaApple){
        List<Apple> result = new ArrayList<>();
        for(Apple apple:appleList){
            if (lamdbaApple.test(apple)){
                result.add(apple);
            }
        }
        return result;
    }

测试方法
    private static void test() {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple("green", 120));
        apples.add(new Apple("red", 160));
        apples.add(new Apple("yellow", 100));
        LamdbaDemo lamdbaDemo = new LamdbaDemo();
        List<Apple> appleList = lamdbaDemo.fileterApples(apples, (Apple a) -> StringUtils.equals("green", a.getColor()));
        System.out.println(appleList.toString());
    }

结果:
[Apple(color=green, weight=120)]

    private static void test2() {
        List<Apple> apples = new ArrayList<>();
        apples.add(new Apple("green", 120));
        apples.add(new Apple("red", 160));
        apples.add(new Apple("yellow", 100));
        LamdbaDemo lamdbaDemo = new LamdbaDemo();
        List<Apple> appleList = lamdbaDemo.fileterApples(apples, (Apple a) -> 150 < apple.getWeight()));
        System.out.println(appleList.toString());
    }
    
结果:
[Apple(color=red, weight=160)]

Lamdba表达式类型检查

Lambda的类型是从使用Lambda的上下文推断出来的。
示例

List<Apple> heavierThan150g =filter(inventory, (Apple a) -> a.getWeight() > 150);

在这里插入图片描述

类型检查过程可以分解为如下所示。

  • 首先,你要找出filter方法的声明。
  • 第二,要求它是Predicate<Apple>(目标类型)对象的第二个正式参数。
  • 第三, Predicate<Apple>是一个函数式接口,定义了一个叫作test的抽象方法。
  • 第四, test方法描述了一个函数描述符,它可以接受一个Apple,并返回一个boolean。
  • 最后, filter的任何实际参数都必须匹配这个要求。

这段代码是有效的,因为我们所传递的Lambda表达式也同样接受Apple为参数,并返回一个
boolean。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值