1. 参考资料

  1. [Java8 实战]电子书于附件下载

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 即可.
  • 图示:
    Java 8 简单使用Java 8 简单使用Java 8 简单使用

以下内容,依照书《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();
    }
    

5. 方法引用

  1. 方法引用就是让你根据已有的方法来创建Lambda表达式.
  2. 方法引用的格式: 方针引用::方法称号
  3. 方法引用主要有3类
    • 指向静态方法 的 方法引用
      • 表现方法: className::func
    • 指向任意类实例指定方法 的 方法引用
      • 表现方法: className::func
    • 指向现有实例的指定方法 的 方法引用
      • 表现方法: instance::func
    public class PersonOpt{
    private String gainTag(Person person){
    return "Tag" + person.getName();
    }
    }
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void testMethodReference() {
    //指向静态方法 的 方法引用
    //这儿指向的是Integer类的静态方法
    //本质: className.func(param)
    //方法: className::func
    Function<String,Integer> f1 = (s) -> Integer.parseInt(s);
    Function<String,Integer> f2 = Integer::parseInt;
    //指向任意类实例指定方法 的 方法引用
    //这儿指向的就是Person类中的非静态方法 getName
    //本质: param.func()
    //方法: className::func
    Function<Person,String> f3 = (p) -> p.getName();
    Function<Person,String> f4 = Person::getName;
    //指向现有实例的指定方法 的 方法引用
    //这儿指向的是PersonOpt实例的非静态方法personOpt
    //本质: instance.func(param)
    //方法: instance::func
    PersonOpt personOpt = new PersonOpt();
    Function<Person,String> f5 = new Function<Person, String>() {
    @Override
    public String apply(Person person) {
    return personOpt.gainTag(person);
    }
    };
    Function<Person,String> f6 = personOpt::gainTag;
    }
    
  4. 对结构函数的引用: ClassName::new
    • 结构函数引用合适用于生产者/Supplier
    • 结构函数的方法就是 void->T
    @RequiresApi(api = Build.VERSION_CODES.N)
    public void testConstructorRef(){
    Supplier<Person> supplier1 = new Supplier<Person>() {
    @Override
    public Person get() {
    return new Person();
    }
    };
    Supplier<Person> supplier2 = () -> new Person();
    //public Person(){} 结构函数的方法就是 void -> T
    Supplier<Person> supplier3 = Person::new;
    }
    
  5. Lambda表达式和方法引用怎样用

    这儿以1个自界说的需求为例.

    我们运用的许多方法都是参数传入,效果回来.

    比方输入3个参数,回来1个效果.

    相同方法的方法有许多个:

    public String f1(int p1,long p2, double p3){
    return **;
    }
    public int f2(int p1,String p2,Person p3){
    return **;
    }
    public Person f3(int p1,long p2,Dog dog){
    return **;
    }
    

    运用lambda表达式及函数引用,可以完结’方法上的复用’.下降代码量.

    • 首要根据入参和回来效果的方法,创建对应的函数式接口.比方3个入参,回来1个效果,可以界说1个包含4个泛型数据的函数式接口.
    • 然后运用Lambda表达式创建该函数式接口的对应实例即可.防止声明过多方法.
    //1:结构1个函数式接口/只需1个抽象方法的接口
    @FunctionalInterface
    public interface CustomInterface<Source,Param1,Param2,Result>{
    Result gainResult(Source source,Param1 param1,Param2 param2);
    }
    //2:创建该函数式接口的实例
    public void testCustomInterface(){
    //原始方法创建
    CustomInterface<String,Integer,Integer,String> c1 = new CustomInterface<String, Integer, Integer, String>() {
    @Override
    public String gainResult(String s, Integer param1, Integer param2) {
    return s.substring(param1,param2);
    }
    };
    //lambda表达式创建
    //(参数列表) -> {return result;}
    CustomInterface<String,Integer,Integer,String> c2 = (source,param1,param2) -> {return source.substring(param1,param2);};
    //(参数列表) -> result
    CustomInterface<String,Integer,Integer,String> c3 = (source,param1,param2) -> source.substring(param1,param2);
    //直接运用方法引用
    //className::func
    //这儿的方法引用归于第二种: param.func()
    //param.func() 的变体 param1.func(param2,param3)
    CustomInterface<String,Integer,Integer,String> c4 = String::substring;
    }
    

8. Stream 待续

9. Optional 待续