JAVA8新特性在Android编程的实践研究(一)

前言

其实Java8已经出现了很久,听闻Java9都准备出了,但是现在普及率并不不是很高,主流的开发环境依然是Java7,甚至有些依然在使用Java5等比较老旧的开发环境。作为一名Android开发者,Java是我们的主要开发语言,我们还是很有必要去了解并且使用最新型的技术,而且Java8中有许多新的特性也是有利于提高我们开发效率的,下面我来一一介绍一下。

本文参考了很多网络上已有的文章,且已注明出处。

介绍

在Android编程中使用Java8新特性,需要先在Android studio中配置一下,具体配置方法,可参考此配置

接口默认方法

在java8中,接口可以添加默认的方法以及静态方法,其类型也是public abstract的类型,实现方式如下:

public interface DataSource<T> {

    List<T> reqeustData(String name);

    @RequiresApi(api = Build.VERSION_CODES.N)
    default void defaultMethod() {
        Log.i("DataSource", "i am defaultMethod");
    }
    
    static void staticMethod(){
        Log.i("DataSource", "i am staticMethod");
    }
}

需要注意的是,接口默认方法现在只能在Android N以上才支持,所以使用的时候要添加声明。

相比于传统的接口,我们在Java8中可以添加默认的方法,而这个默认方法的类型同样是public 的,所以我们可以把它当做正常方式使用,只是无需去实现。在实际项目当中,当一个接口被多个类实现时,我们去拓展接口里面的方法时会导致工程量变大,而当我们引入接口默认方法时,就无需去修改每个类。

当一个接口扩展另外一个包含默认方法的接口的时候,有以下3种处理方式。

  • 完全无视默认方法
    直接继承了上级接口的默认方法
  • 重新申明默认方法
    重新把默认方法申明为抽象方法(无实现,具体子类必需再次实现该方法)
  • 重新实现默认方法
    重写了默认方法的实现,依然是一个默认方法。

我觉得如果对访问权限没有特别要求的,接口基本上能够代替抽象类,而且还能实现多继承。如果对抽象类和接口默认方法感兴趣的童鞋,可以参考这篇文章Java 8 之默认方法(Default Methods)

Lambda表达式

Lambda表达式又称为闭包或匿名方法,我觉得lambda表示式式Java8中最受欢迎的一个特性,因为只要用习惯了这个表达式,就不会再想去用旧的方法来实现了。

Lambda表达式我相信很多人已经听说过,所以我就不做过多介绍,主要说一下我在Android开发中使用到的一些场景:

        button.setOnClickListener(v->showLog("hello"));
        new Thread(()->showLog("hello")).start();
        //多个参数的写法
        List<String> names = Arrays.asList("nickming", "peter", "mike");
        Collections.sort(names, (a, b) -> b.compareTo(a));
        names.forEach(this::showLog);

当然,这只是lambda运用的一部分示例,总结起来lambda的作用就是:

  • 简化代码,可以不用再写匿名内部类,可以将多行代码简化为一行代码。
  • 提供函数式接口,实现函数式编程
  • 提升Java性能

强烈建议在编程中多使用lambda表达式,特别是配合RxJava以及RxAndroid的使用,写起代码真的是行云流水一气呵成。感兴趣的童鞋可以参考这篇文章Lambda表达式的意义

但是使用lambda表达式需要注意几点:

  • 在lambda表达式中可以直接访问外层final变量、实例、静态变量。
  • 访问未标记final的变量时,该变量会具有隐形final属性,如果后续更改变量值,会导致编译不通过。
  • 在lambda表达式中,对于内部实例和静态变量可读可写。
  • lambda无法访问到接口的默认方法。

函数式接口

“函数式接口”是指仅仅只包含一个抽象方法的接口,每一个该类型的lambda表达式都会被匹配到这个抽象方法。因为 默认方法 不算抽象方法,所以你也可以给你的函数式接口添加默认方法。

@FunctionalInterface
public interface Converter<F,T> {
    T convert(F from);
}

Converter<Integer,String> converter= (f)->String.valueOf(f);
converter.convert(1);

Converter<String,Integer> converter3=Integer::new;
Converter<String,Integer> converter2=Integer::valueOf;

这是我们自定义的函数式接口,其实在Java8中已经给很多接口都预置了FunctionalInterface这个关键字,所以这是Java8支持函数式编程的一个重要原因之一,像Runnable、Comparator等接口在Java8中都是标记有FunctionalInterface关键字的。感兴趣的童鞋可以参考这篇文章函数式接口

由于是函数式编程,在我个人理解就是将函数当做编程的基础,区别于我们之前的面向对象的编程,我们所有操作都以函数为基础,函数可以当做其他函数的变量参数,也可以当做其他函数的返回结果。

既然Java8支持函数式编程,所以其提供了方法与构造函数引用这个方式,就像我们可以直接通过Integer::new来实现converter,new是直接实现integer的构造函数,valueOf是其方法引用,只要其返回结果是接口需要实现的返回类型即可。

其实在许多场景下,我们可以直接利用 :: 这个符号来实现方法的引用,例如上文提到的

names.forEach(this::showLog)

这一行代码中,我们省略了for循环,实际上forEach函数式stream特性的一个,下文会提到。forEach函数需要实现Consumer接口,接着在实现其accept方法:

        names.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                showLog(s);
            }
        });
        //lambda表达式
        names.forEach(s->showLog(s));
        //方法引用
        names.forEach(this::showLog);

直接组合利用lambda表达式和方法引用,可以简化代码,表达值简单明了。

常见的函数接口

Java8实际上提供了很多常用的函数式接口,都位于package java.util.function包下面,常用的如下:

  • Predicate 接口只有一个参数,返回boolean类型。该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)
  • **Function **接口有一个参数并且返回一个结果,并附带了一些可以和其他函数组合的默认方法(compose, andThen)
  • **Supplier **接口返回一个任意范型的值,和Function接口不同的是该接口没有任何参数
  • **Consumer **接口表示执行在单个参数上的操作
  • **Comparator **是老Java中的经典接口, Java 8在此之上添加了多种默认方法
  • **Optional **被定义为一个简单的容器,其值可能是null或者不是null。在Java 8之前一般某个函数应该返回非空对象但是偶尔却可能返回了null,而在Java 8中,不推荐你返回null而是返回Optional

我就不仔细分析每个接口的作用了,主要参考函数式接口这篇文章。

其实,当你尝试过使用RxJava和RxAndroid后,你会发现这些x里面的很多概念都是做法都是和Java8中很相似的,都是提供了各种函数操作符,各种函数接口实现。

小结

在这篇文章中,主要是介绍了一下接口默认方法、lambda表达式、函数式接口、方法构造函数的引用这几个特性,在下一篇文章中会着重介绍一下streamDate APIAnnotationCompletableFuture几个特性。

JAVA8新特性在Android编程的实践研究(二)

    原文作者:NKming
    原文地址: https://www.jianshu.com/p/c93329592df1
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞