前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Java8新特性

Java8新特性

原创
作者头像
牧晗
修改2020-06-08 10:08:17
9660
修改2020-06-08 10:08:17
举报
文章被收录于专栏:程序猿专栏程序猿专栏

第1章 引入Lambda表达式

1.1 什么是Lambda表达式

代码语言:txt
复制
Lambda表达式也被称为箭头函数、匿名函数、闭包;



引入的 Lambda 表达式的主要作用就是简化部分匿名内部类的写法;



能够使用 Lambda 表达式的一个重要依据是必须有相应的函数接口。所谓函数接口,是指内部有且仅有一个抽象方法的接口;



Lambda 表达式的另一个依据是类型推断机制。在上下文信息足够的情况下,编译器可以推断出参数表的类型,而不需要显式指名;



语法:() -> {}    参数列表 -> 操作表达式

1.2 为什么用Lambda表达式

代码语言:txt
复制
1. 它不是解决未知问题的新技术

2. 对现有解决方案的语义化优化 

3. 需要根据实际需求考虑性能问题    

第2章. Lambda表达式基础知识

2.1 函数式接口

代码语言:txt
复制
函数式接口,就是Java类型系统中只包含一个接口方法的特殊接口,Java提供了语义化检测注解@FunctionalInterface来进行检测函数式接口的合法性。



当接口中有两个接口方法时便会报错,但可以有多个静态方法和默认方法和从Object继承过来的方法。
代码语言:txt
复制
@FunctionalInterface

public interface LambadaFunction {



    String call(String name);



    default String getAge(String name) {

        return null;

    }

    

    static String getAddress(String name) {

        return null;

    }



}

2.1.1 基本使用

对于一个函数式接口

代码语言:txt
复制
//匿名内部类实现 实现接口的抽象方法

LambadaFunction lambadaFunction = new LambadaFunction(){

    @Override

    public String call(String name) {

        return null;

    }

};     

  
代码语言:txt
复制
//lambda表达式实现 针对函数式接口简单实现

LambadaFunction lambadaSimple = (name) -> {

    if ("zbl".equals(name)) {

        name = "lbz";

    }

    return name;

};

2.1.2 常见的函数式接口

Jdk8提供了java.util.function包,提供了常用的函数式功能接口。

1. java.util.function.Predicate<T>

代码语言:txt
复制
说明:接收参数对象T,返回一个boolean类型结果
代码语言:txt
复制
//源码

@FunctionalInterface

public interface Predicate<T> {



    /\*\*

     \* Evaluates this predicate on the given argument.

     \*

     \* @param t the input argument

     \* @return {@code true} if the input argument matches the predicate,

     \* otherwise {@code false}

     \*/

    boolean test(T t);

    //省略.....

}
代码语言:txt
复制
//使用

Predicate<String> nameBoolean = (name) -> {

    System.out.println(name);

    return "zbl".equals(name);

};



boolean zbl = nameBoolean.test("zbl");

2.java.util.function.Consumer<T>

代码语言:txt
复制
说明:接收一个T类型的参数,不返回任何结果。
代码语言:txt
复制
//源码

@FunctionalInterface

public interface Consumer<T> {



    /\*\*

     \* Performs this operation on the given argument.

     \*

     \* @param t the input argument

     \*/

    void accept(T t);

    //省略..

}    
代码语言:txt
复制
//使用

Consumer<String>  voidConsumer = (name) -> {

    System.out.println(name);

    System.out.println("结束");

};

voidConsumer.accept("lisi");

3.java.util.function.Function<T, R>

代码语言:txt
复制
说明:接收参数对象T,返回结果对象R
代码语言:txt
复制
//源码

@FunctionalInterface

public interface Function<T, R> {



    /\*\*

     \* Applies this function to the given argument.

     \*

     \* @param t the function argument

     \* @return the function result

     \*/

    R apply(T t);

    //省略...

}
代码语言:txt
复制
//使用

Function<String, List<String>> listFunction = (name) -> {

    List<String> list = Lists.newArrayList(name);

    System.out.println(list);

    return list;

};

List<String> strList = listFunction.apply("张三");

4.java.util.function.Supplier<T>

代码语言:txt
复制
说明:不接收参数,提供T对象的创建工厂
代码语言:txt
复制
//源码

@FunctionalInterface

public interface Supplier<T> {



    /\*\*

     \* Gets a result.

     \*

     \* @return a result

     \*/

    T get();

}
代码语言:txt
复制
//使用

Supplier<List<String>> listSupplier = () -> {

    List<String> list = Lists.newArrayList();

    return list;

};

List<String> list = listSupplier.get();

5.java.util.function.UnaryOperator<T>

代码语言:txt
复制
说明:接收参数对象T,返回结果对象T
代码语言:txt
复制
//源码

@FunctionalInterface

public interface UnaryOperator<T> extends Function<T, T> {



    /\*\*

     \* Returns a unary operator that always returns its input argument.

     \*

     \* @param <T> the type of the input and output of the operato

     \* @return a unary operator that always returns its input argument

     \*/

    static <T> UnaryOperator<T> identity() {

        return t -> t;

    }

}
代码语言:txt
复制
//使用

UnaryOperator<String> unaryOperator = (name) -> {

    return name;

};

String zhangsan = unaryOperator.apply("zhangsan");    

6.java.util.function.BinaryOperator<T>

代码语言:txt
复制
说明:接收两个T对象,返回一个T对象结果
代码语言:txt
复制
//源码

@FunctionalInterface

public interface BinaryOperator<T> extends BiFunction<T,T,T> {

    /\*\*

     \* Returns a {@link BinaryOperator} which returns the lesser of two elements

     \* according to the specified {@code Comparator}.

     \*

     \* @param <T> the type of the input arguments of the comparato

     \* @param comparator a {@code Comparator} for comparing the two values

     \* @return a {@code BinaryOperator} which returns the lesser of its operands,

     \*         according to the supplied {@code Comparator}

     \* @throws NullPointerException if the argument is null

     \*/

    public static <T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {

        Objects.requireNonNull(comparator);

        return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;

    }



    /\*\*

     \* Returns a {@link BinaryOperator} which returns the greater of two elements

     \* according to the specified {@code Comparator}.

     \*

     \* @param <T> the type of the input arguments of the comparato

     \* @param comparator a {@code Comparator} for comparing the two values

     \* @return a {@code BinaryOperator} which returns the greater of its operands,

     \*         according to the supplied {@code Comparator}

     \* @throws NullPointerException if the argument is null

     \*/

    public static <T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {

        Objects.requireNonNull(comparator);

        return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;

    }

}
代码语言:txt
复制
//使用

BinaryOperator<Integer> sum = (num1, num2) -> {

    return num1 + num2;

};

Integer apply = sum.apply(1, 3);

2.2 Lambda语法及使用

2.2.1 基本语法

代码语言:txt
复制
    1. 声明:就是和lambda表达式绑定接口类型

    2. 参数:包含在一对圆括号中,和绑定的接口中的参数个数及顺序一致

    3. 操作符:->

    4. 执行代码块:包含在一对大括号中,出现在操作符的右侧

2.2.2 变量的访问操作

1.匿名内部类:

代码语言:txt
复制
说明:在匿名内部类中,this代表的是匿名内部类,而不是LambdaApp这个类
代码语言:txt
复制
public class LambdaApp {

    String s1 = "全局变量";

    

    //1.匿名内部类型中对于变量的访问

    public void testInnerClass() {

        String s2 = "局部变量";

        

        new Thread(new Runnable() {

            String s3 = "内部变量";

            

            @Override

            public void run() {

                //访问全局变量

//                System.out.println(this.s1); //this关键字表示是当前内部类型的对象

                System.out.println(s1);

                

                //访问局部变量

                System.out.println(s2); //不能对局部变量进行数据修改【final】

//                s2 = "不能修改";



                System.out.println(s3);

                System.out.println(this.s3);

            }

        });

    }

2.Lambda表达式:

代码语言:txt
复制
说明:s1是类中的全局变量,this指代的就是类LambdaApp而不是lambda语句块
代码语言:txt
复制
public class LambdaApp {

    String s1 = "全局变量";



    public void testLambda() {

        String s2 = "局部变量Lambda";

        

        new Thread(() -> {

            String s3 = "内部变量Lambda";

            //访问全局变量

//                System.out.println(this.s1); //this关键字表示是当前内部类型的对象

            System.out.println(this.s1);



            //访问局部变量

            System.out.println(s2); //不能对局部变量进行数据修改【final】

//                s2 = "不能修改";



            System.out.println(s3);

            s3 = "Lambda内部变量可以直接修改";

        });

    }

}    

2.3 Lambda表达式运行原理

2.3.1 lambda表达类型检查

声明函数式接口:

代码语言:txt
复制
@FunctionalInterface

public interface MyInterface<T, R> {

    

    R strategy(T t, R r);

    

}

将MyInterface类型作为函数的参数:

代码语言:txt
复制
public static void test(MyInterface<String, List<String>> interParam) {

    List<String> list = interParam.strategy("java", Lists.newArrayList());

    System.out.println(list);

}

lambda表达式实现该参数:

代码语言:txt
复制
 test1((x, y) -> {

    y.add(x);

    return y;

});

lambda表达式的类型检查:

代码语言:txt
复制
当我们将(x,y)->{..}交给test(param)参数时,JVM会推导param参数是一个MyInterface类型的参数,

所以当前的lambda表达式属于MyInterface类型,MyInterface接口就是lambda表达式的目标类型。

参数类型检查:

代码语言:txt
复制
(x,y)->{..} -->MyInterface.strategy(T t,R r),strategy函数需要一个T类型和一个R类型,

我们在把MyInterface当作类型传递给参数时,确定了它的T类型和R类型,确定了它的T类型为String,

R类型为List,然后与lambda表达式进行推导验证,会得出(x,y)执行的就是

strategy(T t,R r)这样一个方法,所以最终推导出来x是属于T类型即String,y属于R类型即List。
代码语言:txt
复制
总结:JVM会根据代码在运行过程中的上下文进行检测,在这里test需要一个MyInterface类型的参数,在调用test时我们传递了一个lambda表达式,

     MyInterface就是lambda表达式的目标类型,接下来会继续根据lambda表达式与绑定的接口进行类型参数的推导,在类型参数进行推导时,

     会验证lambda表达式中的参数个数与顺序是否和接口中定义的参数类型和顺序一致,一致的情况下按照参数的顺序进行确认。

2.3.2 方法重载与lambda表达式

代码语言:txt
复制
public class LambdaApp1 {



    interface Param1 {

        void printInfo(String info);

    }



    interface Param2 {

        void printInfo(String info);

    }



    //定义重载的方法

    public void lambdaMethod(Param1 param1) {

        param1.printInfo("param1");

    }



    public void lambdaMethod(Param2 param2) {

        param2.printInfo("param2");

    }



    public static void main(String[] args) {

        LambdaApp1 app = new LambdaApp1();



        //匿名内部类实现

        app.lambdaMethod(new Param1() {

            @Override

            public void printInfo(String info) {

                System.out.println(info);

            }

        });



        app.lambdaMethod(new Param2() {

            @Override

            public void printInfo(String info) {

                System.out.println(info);

            }

        });



        //lambda表达式实现 会有问题,因为类型检查,而它不知道实现的是哪个接口

        /\*\*

         \* lambdaMethod() -> 方法 -> 重载方法

         \*    -> Param1 函数式接口

         \*    -> Param2 函数式接口

         \*    调用方法 -> 传递Lambda表达式 -> 自动推导->

         \*       -> Param1 | Param2

         \* 编译错误:Error:(49, 12) java: 对lambdaMethod的引用不明确

         \*   方法 lambdaMethod(com.example.testpackage1.LambdaApp1.Param1) 和 

         \*   方法 lambdaMethod(com.example.testpackage1.LambdaApp1.Param2) 都匹配

         \*/

        app.lambdaMethod((info) -> {

            System.out.println(info);

        }) ;



    }



}

第3章 lamdba表达式高级拓展

3.1 方法引用

| 类型 | 语法 | 对应的Lambda表达式 |

| ------------ | ------------------ | ------------------------------------ |

| 静态方法引用 | 类名::staticMethod | (args) -> 类名.staticMethod(args) |

| 实例方法引用 | inst::instMethod | (args) -> inst.instMethod(args) |

| 对象方法引用 | 类名::instMethod | (inst,args) -> 类名.instMethod(args) |

| 构建方法引用 | 类名::new | (args) -> new 类名(args) |

3.2 方法引用举例

3.2.1 静态方法引用

代码语言:txt
复制
//有一个Person类

@Data

public class Person {



    private String name;

    

    private Integer age;



    public static int compareByAge(Person a, Person b) {

        return a.age.compareTo(b.age);

    }

}
代码语言:txt
复制
//现假设,一个部门有30人,把他们存放在一个数组中,并按年龄排序,通常我们可以自己写一个比较器

Person[] rosterAsArray = new Person[30];

// 添加数组元素省略



class PersonAgeComparator implements Comparator<Person> {

    public int compare(Person a, Person b) {

        return a.getBirthday().compareTo(b.getBirthday());

    }

}

        

Arrays.sort(rosterAsArray, new PersonAgeComparator());

Arrays.sort的声明为:public static <T> void sort(T[] a, Comparator<? super T> c),比较器参数Comparator为一个函数式接口,利用上一节Lambda表达式所学知识,可以改写为以下代码:

代码语言:txt
复制
Person[] rosterAsArray = new Person[30];

// 添加数组元素省略



Arrays.sort(rosterAsArray, (a,b) -> a.getAge().compareTo(b.getAge()));

然而,你会发现,Perdon类中已经有了一个静态方法的比较器:compareByAge,因此,我们改用Person类已经提供的比较器:

代码语言:txt
复制
Person[] rosterAsArray = new Person[30];

// 添加数组元素省略



Arrays.sort(rosterAsArray, (a,b) -> Person.compareByAge(a,b));

以上代码,因为Lambda表达式调用了一个已经存在的静态方法,表格中的语法,上面的代码可以最终改写成静态方法引用:

代码语言:txt
复制
Person[] rosterAsArray = new Person[30];

// 添加数组元素省略



Arrays.sort(rosterAsArray, Person::compareByAge);

举个更简单例子:

代码语言:txt
复制
//对一个Integer列表进行排序,因为Integer中已经存在静态的比较方法compare(),因此可以直接用静态方法引用的方式来调用

public class Test {

    public static void main(String[] args) {

        List<Integer> list = Arrays.asList(82,22,34,50,9);

        list.sort(Integer::compare);

        System.out.println(list);

    }

}



//结果:[9, 22, 34, 50, 82]

3.2.2 实例方法引用

代码语言:txt
复制
实例方法引用,顾名思义就是调用已经存在的实例的方法,与静态方法引用不同的是类要先实例化,静态方法引用类无需实例化,直接用类名去调用。
代码语言:txt
复制
@Data

class User {



    private String name;

    private Integer age;



    public User(String name, Integer age) {

        this.name = name;

        this.age = age;

    }

}



public class TestInstanceReference {



    public static void main(String[] args) {



        TestInstanceReference test = new TestInstanceReference();

        User user = new User("欧阳峰",32);

        Supplier<String> supplier = () -> user.getName();

        System.out.println("Lambda表达式输出结果:" + supplier.get());



        Supplier<String> supplier2 = user::getName;

        System.out.println("实例方法引用输出结果:" + supplier2.get());

    }

}



//输出结果:

//Lambda表达式输出结果:欧阳峰

//实例方法引用输出结果:欧阳峰

3.2.3 对象方法引用

代码语言:txt
复制
若Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数时,可以使用对象方法引用。
代码语言:txt
复制
//String的equals()方法:

public boolean equals(Object anObject) {

        if (this == anObject) {

            return true;

        }

        if (anObject instanceof String) {

            String anotherString = (String)anObject;

            int n = value.length;

            if (n == anotherString.value.length) {

                char v1[] = value;

                char v2[] = anotherString.value;

                int i = 0;

                while (n-- != 0) {

                    if (v1[i] != v2[i])

                        return false;

                    i++;

                }

                return true;

            }

        }

        return false;

}





/\*\*

 \* BiPredicate的test()方法接受两个参数,x和y,具体实现为x.equals(y),

 \* 满足Lambda参数列表中的第一个参数是实例方法的参数调用者,而第二个参数是实例方法的参数,因此可以使用对象方法引用。

 \*/

public static void main(String[] args) {



   BiPredicate<String,String> bp = (x, y) -> x.equals(y);

   BiPredicate<String,String> bp1 = String::equals;



   boolean test = bp1.test("xy", "xx");

   System.out.println(test);

}

3.2.4 构造方法引用

代码语言:txt
复制
注意:需要调用的构造器的参数列表要与函数式接口中抽象方法的参数列表保持一致。
代码语言:txt
复制
//要获取一个空的User列表

Supplier<List<User>> userSupplier = () -> new ArrayList<>();

List<User> user = userSupplier.get();



Supplier<List<User>> userSupplier2 = ArrayList<User>::new;    // 构造方法引用写法

List<User> user2 = userSupplier.get();

3.3 StreamAPI

代码语言:txt
复制
步骤1、创建Stream - 从一个数据源,如集合、数组中获取流。



步骤2、中间操作 - 一个操作的中间链,对数据源的数据进行操作。



步骤3、终止操作 - 一个终止操作,执行中间操作链,并产生结果。

3.3.1 获取stream对象

代码语言:txt
复制
1. 从集合或者数组中获取

   Collection.stream(), //串行 如:list.stream()

   Collection.parallelStream()  //并行

   Arrays.stream(T t)    

       

2. BufferReade

   BufferReader.lines() -> stream()

       

3. 静态工厂

   java.util.stream.IntStream#range()..

   java.nio.file.Files#walk()....



4. 自定义构建

   java.util.Spliterator    

3.3.2 中间操作API{ intermediate}

代码语言:txt
复制
操作结果是一个stream,中间操作可以有一个或者多个连续的中间操作,需要注意的是,

中间操作只记录操作方式,不做具体执行,直到结束操作发生时,才做数据的最终执行。



\*中间操作过程\*:

无状态:数据处理时,不受前置中间操作的影响,主要包括[map/filter/peek/parallel/sequential/unordered]

有状态:数据处理时,受到前置中间操作的影响,主要包括[distinct/sorted/limit/skip]

3.3.3 终结操作|结束操作{Terminal}

代码语言:txt
复制
需要注意:

    

1.一个Stream对象,只能有一个Terminal操作,这个操作一旦发生,就会真实处理数据,生成对应的数据;

    

2.终结操作:

  非短路操作:当前的stream对象必须处理完集合中所有数据,才能得到处理结果;主要包括:

            forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterato

    

  短路操作:当前的stream对象在处理过程中,一旦满足某个条件,就可以得到结果

          anyMatch/allMatch/findFirst/findAny等  

3.4 Stream操作集合中的数据

3.4.1 其它类型->stream对象

代码语言:txt
复制
//多个数据

Stream stream = Stream.of("admin", "Jack", "Tom");



//数组

String[] strArr = new String[]{"admin", "Jack"};

Stream stream1 = Arrays.stream(strArr);



//列表

List<String> lists = Lists.newArrayList();

Stream<String> stream2 = lists.stream();



//集合

Set<String> set = Sets.newHashSet();

Stream<String> stream3 = set.stream();



//Map

Map<String, Integer> map = Maps.newHashMap();

Stream<Map.Entry<String, Integer>> stream4 = map.entrySet().stream();

在数据运算中,会对基本数据类型进行频繁的装箱拆箱操作,所以对于基本类型stream进行了一些基本的封装:

代码语言:txt
复制
IntStream.of(new int[]{10,20,30}).forEach(System.out::println);

IntStream.range(1, 5).forEach(System.out::println);

3.4.2 stream对象->其它类型

代码语言:txt
复制
Stream streams = Stream.of("admin", "Jack", "Tom");

//数组

Object[] objx = streams.toArray(String[]::new);



//字符串

String str = streams.collect(Collectors.joining()).toString();



//列表

List<String> listx = (List<String>) stream.collect(Collectors.toList());



//集合

Set<String> setx = (Set<String>) streams.collect(Collectors.toSet());



//Map

Map<String, String> mapx = (Map<String, String>) streams.collect(Collectors.toMap(x -> x, y -> "value:" + y));

3.4.3 stream常见API操作

1. map()方法

代码语言:txt
复制
    /\*\*

     \* map()方法的源码

     \* Returns a stream consisting of the results of applying the given function to the elements of this stream.

     \* @param <R> The element type of the new stream

     \* @param mapper a <a href="package-summary.html#NonInterference">non-interfering</a>,

     \*               <a href="package-summary.html#Statelessness">stateless</a>

     \*               function to apply to each element

     \* @return the new stream

     \*/

    <R> Stream<R> map(Function<? super T, ? extends R> mapper);

map()方法的参数为Function(函数式接口)对象,map()方法将流中的所有元素用Function对象进行运算,生成新的流对象(流的元素类型可能改变)。举例如下:

代码语言:txt
复制
    public static void main(String[] args) {  

        List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);

        //用map()方法计算了所有数组元素的绝对值并生成了一个新的流,然后再用forEach()遍历打印。

        numbers.stream().map( n -> Math.abs(n)).forEach(n ->  System.out.println("Element abs: " + n));

    }

2. flatMap()方法

代码语言:txt
复制
跟map()方法不同的是,Function函数的返回值类型是Stream<? extends R>类型,而不是R类型,即Function函数返回一个Stream流,

这样flatMap()能够将一个二维的集合映射成一个一维的集合,比map()方法拥有更高的映射深度
代码语言:txt
复制
//源码

<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
代码语言:txt
复制
//有一个字符串数组:其有三个元素,每个元素有两个数组并用空格隔开,如果每个元素以空格分割成2个元素,并遍历打印这6个元素

List<String> list = Arrays.asList("1 2", "3 4", "5 6");
代码语言:txt
复制
//用flatMap()方法如下:

list.stream().flatMap(item -> Arrays.stream(item.split(" "))).forEach(System.out::println);





//用map()方法返回了一个“流中流”,需要在每个Stream元素遍历时,再加一层forEach进行遍历

 list.stream().map(item -> Arrays.stream(item.split(" "))).forEach(n ->n.forEach(System.out::println));

3.filter()方法

代码语言:txt
复制
    /\*\*

     \* 源码

     \* Returns a stream consisting of the elements of this stream that match the given predicate.

     \*

     \* <p>This is an <a href="package-summary.html#StreamOps">intermediate operation</a>.

     \*

     \* @param predicate a <a href="package-summary.html#NonInterference">non-interfering</a>,

     \*                  <a href="package-summary.html#Statelessness">stateless</a>

     \*                  predicate to apply to each element to determine if it  should be included

     \* @return the new stream

     \*/

    Stream<T> filter(Predicate<? super T> predicate);

filter()方法的参数为Predicate(函数式接口)对象,再lambda表达式的讲解中我们提到过这个接口,一般用它进行过滤

代码语言:txt
复制
    public static void main(String[] args) {  

        List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);

      

        long count = numbers.parallelStream().filter(i -> i>0).count();

        

        System.out.println("Positive count: " + count);

    }

4.reduce()方法

代码语言:txt
复制
//reduce操作又称为折叠操作,用于将流中的所有值合成一个。reduce()方法的源码

Optional<T> reduce(BinaryOperator<T> accumulator);
代码语言:txt
复制
reduce()方法参数为BinaryOperator类型的累加器(它接受两个类型相同的参数,返回值类型跟参数类型相同),返回一个Optional对象。

实际上,Stream API中的mapToInt()方法返回的IntStream接口有类似的 average()、count()、sum()等方法就是做reduce操作,

类似的还有mapToLong()、mapToDouble() 方法。当然,我们也可以用reduce()方法来自定义reduce操作。



 

//例如我们用reduce()方法来进行整数数组求和操作    

public static void main(String[] args) {

    List<Integer> numbers = Arrays.asList(-1, -2, 0, -1, 4, 5, 1);

    

    Integer total = numbers.stream().reduce((t, n) -> t + n).get();

    

    System.out.println("Total: " + total);

}    

5.collect()方法

代码语言:txt
复制
    /\*\*

     \* @param <R> the type of the result

     \* @param <A> the intermediate accumulation type of the {@code Collector}

     \* @param collector the {@code Collector} describing the reduction

     \* @return the result of the reduction

     \*/

    <R, A> R collect(Collector<? super T, A, R> collector);
代码语言:txt
复制
collect()方法的参数为一个java.util.stream.Collector类型对象,可以用java.util.stream.Collectors工具类提供的静态方法来生成,

Collectors类实现很多的归约操作,如Collectors.toList()、Collectors.toSet()、Collectors.joining()(joining适用于字符串流)等

    



//用map()方法生成新的流,再用collect()方法返回原数组的绝对值数组

public static void main(String[] args) {  

    List<Integer> numbers = Arrays.asList(-1, -2, 0, 4, 5);

    

    List<Integer> abss = numbers.stream().map( n -> Math.abs(n)).collect(Collectors.toList());

    

    System.out.println("Abs list: " + abss);

}    

第4章 Optional类

4.1 背景

先来看一下不使用Optional类时,我们为了防止NullPointerException会怎么处理

代码语言:txt
复制
@Data

public class Person {



    private String username;



    public Person getParent() {

        return new Person();

    }



    //原始逻辑判断空代码

    public String getParentName(Person son) {

        if (son != null) {

            Person parent = son.getParent();

            if (parent != null) {

                return parent.getUsername();

            } else {

                return "---";

            }

        }

        return "---";

    }



    //Optional代码

    public String getParentNameOptional(Person son) {

        return Optional.ofNullable(son).map(Person::getParent).map(Person::getUsername).orElse("---");

    }



}

4.2 Optional类简介

代码语言:txt
复制
java.util.Optional类的引入很好的解决空指针异常,类声明如下:

    public final class Optional {}



java.util.Optional类是一个封装了Optional值的容器对象,Optional值可以为null,如果值存在,调用isPresent()方法返回true,调用get()方法可以获取值。



通过源代码会发现,它并没有实现java.io.Serializable接口,因此应避免在类属性中使用,防止意想不到的问题。

    

除了Optional类之外,还扩展了一些常用类型的Optional对象,比如:OptionalDouble、OptionalInt、OptionalLong。    

4.3 创建Optional对象

4.3.1 创建对象

代码语言:txt
复制
创建Optional对象有三种方法:empty()、of()、ofNullable(),均为静态方法。

1.如果Optional对象没有值则用empty()方法

代码语言:txt
复制
Optional empty = Optional.empty();

2.如果确定Optional对象的值不为null,则可用of()方法

代码语言:txt
复制
Optional stringOptional = Optional.of("Hello 公众号:假如我是你");

3.如果不确定Optional对象的值是否为null,则可用ofNullable()

代码语言:txt
复制
比如上面,不确定Person对象是不否null,就用了ofNullable()方法。当然,也可以直接给该方法传null

    

Optional ofNullOptional = Optional.ofNullable(null);



此时,通过调用其isPresent方法可以查看该Optional中是否值为null。

boolean bool = ofNullOptional.isPresent();

System.out.println(bool);

4.3.2 获取对象的值

1.get获取Optional中的值

代码语言:txt
复制
此时如果直接调用get方法获取值,则会抛出异常 java.util.NoSuchElementException: No value present

ofNullOptional.get();    



需要另外一个方法的辅助:isPresent()。该方法可判定Optional中是否有值,如果有则返回true,如果没有则返回false。

Optional ofNullable1 = Optional.ofNullable(null);

boolean flag = ofNullable1.isPresent();

if (flag) {

   ofNullable1.get();

}    

2.map获取Optional中的值

代码语言:txt
复制
//对于对象操作,也可以通过map来获取值

Optional.ofNullable(son).map(Person::getParent)



注:map方法,如果有值,则对其执行调用映射函数得到返回值。如果返回值不为null,

   则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。   

3.flatMap获取Optional中的值

代码语言:txt
复制
如果有值,则返回Optional类型返回值,否则返回空Optional。flatMap与map方法类似。

但flatMap中的mapper返回值必须是Optional。调用结束时,flatMap不会对结果用Optional封装。



Optional sonOptional = Optional.ofNullable(son);

sonOptional.flatMap(OptionalTest::getOptionalPerson);    

4.orElse获取Optional中的值

代码语言:txt
复制
orElse方法,如果有值就返回,否则返回一个给定的值作为默认值;

Optional.empty().orElse("--"); 

等价于 str != null ? str : "--"

5.orElseGet获取Optional中的值

代码语言:txt
复制
orElseGet()方法与orElse()方法作用类似,但生成默认值的方式不同。该方法接受一个Supplier函数式接口参数,用于生成默认值;



//这里可以处理更多的业务逻辑    

Optional.empty().orElseGet(() -> {

    String a = "1";

    String b = "1";

    return a + b;

});    

6.orElseThrow获取Optional中的值

代码语言:txt
复制
orElseThrow()方法与get()方法类似,当值为null时调用会抛出NullPointerException异常,但该方法可以指定抛出的异常类型。



Optional.empty().orElseThrow(()-> new RuntimeException("请先关注公众号!"));    

7.filter()方法过滤

代码语言:txt
复制
filter()方法可用于判断Optional对象是否满足给定条件,一般用于条件过滤:



Optional<String> who = Optional.of("我是谁, 不一样的存在").filter((val) -> {

    return val.contains("我是谁");

});



System.out.println(who.get());    

示例:

代码语言:txt
复制
     public static void main(String[] args) {

        UserService userService = new UserService();

        User user = userService.getUserByName("张三");

        /\*\*

         \* jdk8以前的做法

         \*/

        if (user != null) {

            System.out.println("user = " + user);

        } else {

            System.out.println("为空的业务逻辑");

        }

        /\*\*

         \* 使用Optional

         \* 与if ...else差别不大

         \*/

        Optional<User> optional = Optional.ofNullable(user);

        if (optional.isPresent()) {

            System.out.println("user不为null");

        } else {

            System.out.println("user为Null的业务逻辑");

        }



        /\*\*

         \* 如果optional中的user不为空,则会执行,否则不执行

         \* 某些情况下,这样的逻辑刚好可用

         \*/

        optional.ifPresent((t -> {

            System.out.println("t = " + t);

        }));



        /\*\*

         \* 使用map方法,进行业务逻辑处理,

         \*/

        optional.map(t -> {

            // 业务逻辑处理

            return "user不为空";

        }).orElse(null);

    }

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 第1章 引入Lambda表达式
    • 1.1 什么是Lambda表达式
      • 1.2 为什么用Lambda表达式
      • 第2章. Lambda表达式基础知识
        • 2.1 函数式接口
          • 2.1.1 基本使用
          • 2.1.2 常见的函数式接口
        • 2.2 Lambda语法及使用
          • 2.2.1 基本语法
          • 2.2.2 变量的访问操作
        • 2.3 Lambda表达式运行原理
          • 2.3.1 lambda表达类型检查
          • 2.3.2 方法重载与lambda表达式
      • 第3章 lamdba表达式高级拓展
        • 3.1 方法引用
          • 3.2 方法引用举例
            • 3.2.1 静态方法引用
            • 3.2.2 实例方法引用
            • 3.2.3 对象方法引用
            • 3.2.4 构造方法引用
          • 3.3 StreamAPI
            • 3.3.1 获取stream对象
            • 3.3.2 中间操作API{ intermediate}
            • 3.3.3 终结操作|结束操作{Terminal}
          • 3.4 Stream操作集合中的数据
            • 3.4.1 其它类型->stream对象
            • 3.4.2 stream对象->其它类型
            • 3.4.3 stream常见API操作
        • 第4章 Optional类
          • 4.1 背景
            • 4.2 Optional类简介
              • 4.3 创建Optional对象
                • 4.3.1 创建对象
                • 4.3.2 获取对象的值
            相关产品与服务
            容器服务
            腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档