Java 8 简单使用

1. 参考资料

  1. [Java8 实战]电子书于附件下载
  2. 这篇最全 Java 8 讲解,有没有之一先看后说!

2. 内容深度

  1. 简单,不涉及复杂及高阶用法
  2. 仅介绍Java8特性中最易上手,最快见效的用法

3. Java8最有用的特性

1. Lambda表达式及方法引用让代码更简洁,降低代码行数.

2. Stream做为数据集的高级迭代器,可以方便地对一组数据进行遍历,筛选及统计.并支持并行处理.

3. interface支持默认方法,可以灵活地对interface进行扩展.

4. 用Optional取代null,降低空指针异常的发生.

4. 在AS中如何利用Java8特性

  1. 直接在要使用Java8特性的moudle中的build.gradle进行配置即可
    android {
        ***
        compileOptions {
            sourceCompatibility JavaVersion.VERSION_1_8
            targetCompatibility JavaVersion.VERSION_1_8
        }
    }
    复制代码
  2. 直接在整个项目中搜索 “可以替换为lambda表达式的匿名内部类” ,一键降低代码量
  • Android Studio工具栏 -> Analyze -> Run Inspection by Name
  • 输入 replaced with lambda ,选中 Anonymous type can be replaced with lambda
  • 范围选择 Whole project -> OK
  • 待AS查询完毕,直接执行 Replace with lambda 即可.
  • 图示:
    step1.png
    step2.png
    step3.png

以下内容,按照书《Java 8 实战》章节顺序,筛选部分内容依次陈述.

5. 引入Java8后,我们的代码有什么变化: 简单地说,引入Lambda后代码量降低了.

流,方法引用,Option等内容后续陈述.

  1. 以创建Thread为例
    //原始写法
    Thread thread1 = new Thread(new Runnable() {
        @Override
        public void run() {
            Log.d(TAG, "old");
        }
    });
    //Java8写法
    Thread thread2 = new Thread(() -> Log.d(TAG, "java8"));
    复制代码
  2. 以设置View点击监听为例
    //原始写法
    view.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Log.d(TAG, "old");
        }
    });
    //Java8写法
    view.setOnClickListener(v -> Log.d(TAG, "java8"));
    复制代码

6. 接口支持默认方法

1. 什么是默认方法

  1. 接口中以default修饰,接口自身已经实现的方法.
  2. 接口I中的默认方法,I的实现类C会直接继承,C不必再次实现.
  3. 实例
    public interface I{
        void exe();
        //默认方法,接口本身已实现
        default void f1(){
            Log.d("Interface","default Method");
        }
    }
    public class C implements I{
        @Override
        public void exe() {
        }
        //默认方法,接口实现类不必实现
    }
    复制代码

2. 为什么要提供默认方法

  1. 默认方法最主要的目的,是让类库的设计者可以放心地对原有接口进行扩展.避免原有接口增加了新的方法,所有的实现类都要增加其实现;
  2. 除了便于接口扩展,自己在做代码精简时的用法

    有时候定义1个接口,为了扩展性会声明一些方法,现有功能下并不会调用,或者不同场景下,会调用不同的方法;

    我们会使用该接口的多个实现类实例/匿名内部类用于传参.

    如果接口中都是传统的抽象方法,就会导致代码的臃肿,或者冗余.

    通过在接口中声明default方法,既能保留待扩展的方法,也能更灵活地对不同的默认方法进行重写,极大地降低代码量.

    public interface OriInterface {
        void f1();
        void f2();
        void f3();
        void f4();
    }
    public interface DefaultInterface {
        void f1();
        default void f2(){};
        default void f3(){};
        default void f4(){};
    }
    public void testOriInterface(OriInterface oriInterface){
    }
    public void testOriInterfaceFunc2(OriInterface oriInterface){
        oriInterface.f2();
    }
    public void testDefaultInterface(DefaultInterface defaultInterface){
    }
    public void testDefaultInterfaceFunc2(DefaultInterface defaultInterface){
        defaultInterface.f2();
    }
    public void testInterface(){
        //测试原始接口
        testOriInterface(new OriInterface() {
            @Override
            public void f1() {
            }
            @Override
            public void f2() {
            }
            @Override
            public void f3() {
            }
            @Override
            public void f4() {
            }
        });
        testOriInterfaceFunc2(new OriInterface() {
            @Override
            public void f1() {
            }
            @Override
            public void f2() {
                Log.d("TestInterface","ori interface. f2.");
            }
            @Override
            public void f3() {
            }
            @Override
            public void f4() {
            }
        });
        //测试包含默认方法的接口
        testDefaultInterface(new DefaultInterface() {
            @Override
            public void f1() {
            }
        });
        testDefaultInterfaceFunc2(new DefaultInterface() {
            @Override
            public void f1() {
            }
            @Override
            public void f2() {
                Log.d("TestInterface","default interface. f2.");
            }
        });
    }
    复制代码

7. Lambda表达式

1. Lambda表达式是什么

  1. Lambda表达式是 可以简洁地表示可以传递的匿名函数的一种方式.
  2. Lambda表达式没有名称,但有 参数列表,函数主题,返回类型,可能还有1个可以抛出的异常列表.
  3. Lambda表达式可以作为参数传递给方法.
  4. 怎么理解作为参数进行传递

    我们之前想要将一段逻辑/代码块传递出去,最常用的就是定义一个接口I,方法func参数中包含I的实例,func方法体中调用I实例的指定方法,也就是常用的匿名内部类/监听器写法.

    public interface I{
        void exe();
    }
    public void func(I i){
        i.exe();
    }
    func(new I() {
        @Override
        public void exe() {
            Log.d("I","exe");
        }
    });
    复制代码

    匿名内部类/监听器接口缺点在于代码很臃肿,而lambda表达式就是对其进行了简化.

    func(()->Log.d("I","exe"));
    复制代码

    Lambda表达式并不能让你实现以前不能实现的功能

2. Lambda表达式的几种样式

  1. 总体样式: (参数列表) -> {代码块; return **;}
    (参数列表) -> 单行代码
    
    (参数列表) -> {代码;}
    
    (参数列表) -> {
        代码;
        return 返回值;
    }
    
    (参数列表) -> {return 返回值;}
    
    (参数列表) -> 返回值
    复制代码

3. Lambda表达式可以用在哪里: 在函数式接口上使用.

  1. 什么是函数式接口: 函数式接口就是仅有1个抽象方法的接口
    • 注意只针对抽象方法数量进行限制.
    • 一个接口即使包含很多默认/default方法,只有接口中只定义了1个抽象方法,依然是函数式接口.
    • DefaultInterface虽然有很多默认方法,但仅包含1个抽象方法,也属于函数式接口.
    @FunctionalInterface
    public interface DefaultInterface {
        void f1();
        default void f2(){};
        default void f3(){};
        default void f4(){};
    }
    复制代码
    • 对于函数式接口,建议添加注解@FunctionalInterface ,但不是必须的.
  2. Lambda表达式允许直接以内联的形式为函数式接口的抽象方法提供实现,并将整个Lambda表达式作为该函数式接口的1个实例.
    • 由此可见,传递指定接口实例,使用匿名内部类,和使用Lambda表达式本质是一样的,都是传递1个”实例”.
    • Lambda表达式只是让代码更简洁.
  3. 函数式接口的抽象方法的签名基本上就上Lambda表达式的签名.
    • 根据这个规则,对照函数式接口的’方法形式’,就可以写出对应的Lambda表达式
    • (type p1,type p2, ***) -> { 方法体; }
    @FunctionalInterface
    public interface FuncI1{
        void f();
    }
    @FunctionalInterface
    public interface FuncI2{
        void f1(int p1,int p2);
    }
    
    private void testLambdaFuncSign() {
        //以Runnable为例
        //Runnable就属于函数式接口
        //@FunctionalInterface
        //public interface Runnable {
        Runnable r1 = new Runnable() {
            @Override
            public void run() {
            }
        };
        Runnable r2 = () -> {
        };
    
        //以自定义函数式接口为例
        FuncI1 funcI11 = new FuncI1() {
            @Override
            public void f() {
            }
        };
        FuncI1 funcI12 = () -> {};
        FuncI2 funcI21 = new FuncI2() {
            @Override
            public void f1(int p1, int p2) {
            }
        };
        FuncI2 funcI22 = (p1, p2) -> {};
    }
    复制代码

4. Java8新增的函数式接口

  1. Predicate
    • Predicate接收泛型T实例,返回boolean值. T->boolean.
    • Predicate常用于对流中的数据进行过滤,及其他根据1个实例返回boolean的场景
    • 示例
    @FunctionalInterface
    public interface Predicate<T> {
        boolean test(T t);
    }
    
    public class Person{
        public String name = "Li Lei";
        public String getName() {
            return name;
        }
        public void setName(String name) {
            this.name = name;
        }
    }
    @RequiresApi(api = Build.VERSION_CODES.N)
    public boolean testPerson(Person person, Predicate<Person> predicate){
        return predicate.test(person);
    }
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void testPredicate(){
        Predicate<Person> personPredicate = person -> person.getName().equals("Han Meimei");
        testPerson(new Person(),personPredicate);
    }
    复制代码
  2. Consumer
    • Consumer接收一个泛型T实例,无返回. T->void
    • 从命名即可看出,单纯消费一个实例.
    • 示例
    @FunctionalInterface
    public interface Consumer<T> {
        //单纯地消费1个实例
        void accept(T t);
    }
    
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void testConsumer(){
        List<String> list = Arrays.asList("1","2","3");
        Consumer<String> consumer = s -> Log.d("TestConsumer","当前项:" + s);
        list.forEach(consumer);
    }
    复制代码
  3. Function
    • Function接收1个泛型T实例,返回1个泛型R实例. T->R
    • 可以理解为常规的 输入->输出.
    • 示例
    @FunctionalInterface
    public interface Function<T, R> {
        //接收T实例,返回R实例
        R apply(T t);
    }
    
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void testFunction(){
        /*
        Function<String, Integer> function = new Function<String, Integer>() {
            @Override
            public Integer apply(String s) {
                return Integer.parseInt(s);
            }
        };
        */
        //这里再次复习一下使用Lambda表达式表示 函数式接口实例
        Function<String, Integer> function = s -> Integer.parseInt(s);
        int result = function.apply("1000");
    }
    复制代码
  4. Supplier
    • Supplier不接收实例,直接生成一个泛型T实例. void->T
    • 从名字就可以看出,是一个生产者
    • 示例
    @FunctionalInterface
    public interface Supplier<T> {
        T get();
    }
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void testSupplier() {
        Supplier<Person> supplier = () -> new Person();
        Person person = supplier.get();
    }
    复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享