《Java8 实战》笔记——3.重构&优化&Optional

第 8 章

重构、测试和调试

改善代码?

  • 集合类的操作→stream;
  • 匿名类->Lambda表达式(注意:匿名类的this指匿名类自身,而lambda的this指的是lambda"对象"(把lambda看做成员变量)所在的类,因此Lambda{...}内部不能定义与外部相同名字的变量,会编译错误)
eg:
{...
  int a = 10;
  Runnable r = new Runnable(){int a = 10;...}  //**编译错误**
}
  • 方法引用代替{...}中的语句,将{...}中的语句抽象成方法,这样就可以使用方法引用;
  • 在作为方法参数时,匿名类的类型是确定的(因为是new出来的),但Lambda类型是"匹配出来的",因此在函数涉及重载的时候,lambda的类型会不确定
eg:
    interface Task {
        public void excute();

        public void doSth(Runnable r) {
            r.run();
        }
        //如果传入lambda则它的类型无法指定;
        public void doSth(Task t) {
            t.excute();
        } 
    }
  • Lambda在设计模式中的应用,如策略策略设计模式/模板方法模式-替换定义的多个类职责链模式-Function .andThen()、工厂方法模式-把lambda ClassA::new,ClassB::new看做对象放到一个map里面,用get通过名字去取不同类的对象;
  • lambda不方便调试(没有类名),在stream操作中可以使用peek方法,有点像断点调试;
eg:
    @Test
    public void func_04() {
        System.out.println("step-1:");
        Stream.of(1, 2, 3, 4, 5).map(i -> i + 10).forEach(System.out::println);//使用forEach调试,但查看结果,不能查看每一步的结果;
        System.out.println("step-2:");
        Stream.of(1, 2, 3, 4, 5).map(i -> i + 10).filter(i -> i < 13).forEach(System.out::println);//使用forEach调试,但查看结果,不能查看每一步的结果,只能一步步地打印,很麻烦;

        System.out.println("use-peak():");
        Stream.of(1, 2, 3, 4, 5).map(i -> i + 10).peek(i -> System.out.println("step-1:" + i)).filter(i -> i < 13).peek(i -> System.out.println("step-2:" + i)).count();//使用peak调试,注意最后要加上"终端操作"
    }

第 9 章

default方法

  1. 为什么要新加default方法?
    ——为了兼容类库,为了在接口中能新加入方法而不需要重构(在其子类中实现新抽象方法)以前的代码;

  2. default方法冲突的3条规则
    (1) 当两个父类接口中,有一个的default方法更加"具体"(这里B继承A,比A的hello方法更具体),会继承那个更具体的方法;
    (2) 子类继承extends一个父类并且implements两个具有相同default方法的父类接口,extends类优先
    (3) 子类通过在覆盖的方法内,显示的调用某个父类的default方法来避免冲突,Father_A.super.hello();

eg:

public class TestDefaultMethod_02 {
    //测试default方法遇上菱形继承
    public static void main(String[] args) {
//default遇上多继承(相同的default方法声明)
        new TDM_C().hello();//(1)当两个父类接口中,有一个的default方法更加"具体"(这里B继承A,比A的hello方法更具体),会继承那个更具体的方法;
        new TDM_E().hello();//(2)子类继承extends一个父类并且implements两个具有相同default方法的父类接口,extends类优先;
        new TDM_H().hello();//(3)子类通过在覆盖的方法内,显示的调用某个父类的default方法来避免冲突,Father_A.super.hello();
    }
}

interface TDM_A {
    default void hello() {
        System.out.println("hello A.");
    }
}

interface TDM_B extends TDM_A {//**class B的方法比A的hello更"具体"**

    default void hello() {
        System.out.println("hello B.");
    }
}

class TDM_C implements TDM_A, TDM_B {

}

class TDM_D {
    public void hello() {
        System.out.println("hello D.");
    }
}

class TDM_E extends TDM_D implements TDM_A, TDM_B {//**类优先,继承D的方法**

}

interface TDM_f {
    default void hello() {
        System.out.println("hello f.");
    }
}

interface TDM_G {
    default void hello() {
        System.out.println("hello G.");
    }
}

class TDM_H implements TDM_f, TDM_G {
    public void hello() {
        TDM_f.super.hello();**//显示调用父类接口的default方法,避免冲突;**
        TDM_G.super.hello();
    }
}

第 10 章

用Optional取代null

  1. 非空判断null带来的问题
    多层嵌套非空判断;可读性差;nul可以赋值给任意类型对对象;

  2. Optional是一个存放变量的容器,通过泛型与变量类型绑定,eg:Optional<String>替换String,空值为Optional.empty(),不再是一个null;Optional的目的不是撤掉消除null,而是能清晰地界定出变量值的缺失原因,是结构/算法逻辑/数据本身的问题,能帮助设计出更加普适的API;

  3. Optional用法

初始化

Optional.empty();

Optional.of(T t);

Optional.ofNullable(T t);

eg:

 @Test
    public void func_01() {//测试Optional对象初始化
        Optional<TO_Person> p1 = **Optional.empty() **;//(1)申明一个值为null的Optional
        p1.ifPresent(System.out::print);//加了这一句就不需要toString里面的非空判断

        TO_Person person = new TO_Person("Li", 18);
        Optional<TO_Person> p2 = **Optional.of(person) **;//(2)of方法,传递一个对象(值)
        System.out.println(p2.isPresent());

        person = null;//**修改person不会影响Optional的第一次赋值**(存的是副本-深度复制)
        System.out.println(p2.isPresent());

        // p2 = Optional.of(null);
        // System.out.println(p2.isPresent());//这里会NullPointerException,因为of方法不接受空值null;

        Optional<TO_Person> p3 = **Optional. ****ofNullable(null) **;//(3)ofNullable接收空值null;
    }

map/flatMap方法

    @Test
    public void func_02() {
        TO_Person person = new TO_Person("Lee", 28);
        Optional<TO_Person> p1 = Optional.of(person);
        Optional<String> p2 = p1. **flatMap **(TO_Person::getName);//**(1)* map方法与流Stream的map方法用法一样**;

        // 需求:需要获取一个Person对象的name,根据这个name来新new一个Person对象,打印它的age属性
        //**这种操作像一个"管道"**,先对person对象map成String name,再将String给map成person对象,最后再map成int age;

        // Optional<Integer> p3 = p1.map(TO_Person::getName).map(TO_Person::getPersonByName).map(TO_Person::getAge); //**编译错误**,**因为第二个map的传入的对象类型是Optional<Optional<String>>**;

        // 之前在Stream中遇到类似的问题,**即流元素本身也是个流,需要把流"扁平化"**,让流元素的值合在一起成一个流,使用的flatMap方法,这里也是类似的,对象放在Optional容器里面,访问它的值(对象)内部的属性时,这个属性对应一个Optional<Optional<对象的属性所属的类>>,即Optional的值也是一个Optional,通过flatMap将它"扁平化"则变成一层Optional,即Optional<对象的属性所属的类>;
        int age = p1. **flatMap **(TO_Person::getName).flatMap(TO_Person::getPersonByName).flatMap(TO_Person::getAge).orElse(0);
    }

filter过滤

    @Test
    public void func_03(){
        TO_Person person = new TO_Person("Lee", 20);
        Optional<TO_Person> p1 = Optional.of(person);

        int age = p1.flatMap(TO_Person::getName).flatMap(TO_Person::getPersonByName).flatMap(TO_Person::getAge).orElse(0);
        System.out.println(age);

        int age1 = p1.flatMap(TO_Person::getName).flatMap(TO_Person::getPersonByName).flatMap(TO_Person::getAge).filter(a -> a < 20).orElse(0); //过滤<20的age
        System.out.println(age1);
    }

Optional序列化

public final class Optional<T> {...}说明Optional没有实现java.io.Serlizable接口,不能序列化
解决:提供一个方法getOptional:return Optional.ofNullable(name);

*从Optional中取值的方法

@Test
public void func_04() {
  TO_Person person = new TO_Person("Lee", 20);
  TO_Person person1 = null;
  Optional<TO_Person> p = Optional.ofNullable(person);

  System.out.println(p.get());//**(1)get方法从Optional容器中取值,但是最不安全,值为null会报错NoSuchElementException**
  Optional<TO_Person> p1 = Optional.ofNullable(person1);

  System.out.println(p1.orElse(new TO_Person("--", 0)));//**(2)orElse方法,如果值为null(Optional.empty())返回一个默认值(这个值是<u>事先初始化好的</u>);**
  p1 = Optional.ofNullable(person1);

  System.out.println(p1.orElseGet(() -> new TO_Person("--", 1)));//**(3)orElseGet方法,传入一个supplier,如果值为null(Optional.empty())返回一个默认值,是orElse方法的"延迟版",当null时才会调用Consumer;**

  //System.out.println(p1.orElseThrow(()->new ArrayIndexOutOfBoundsException()));//**(4)orElseThrow,和get类似,在Optional值为空时候抛出指定Runtime异常;**

  p.ifPresent(x-> System.out.println(p+" existed"));//**(5)ifPresent,当值存在时才去作为参数传入Consumer执行某个操作,值为null则无操作;**
  p1.ifPresent(x-> System.out.println(" existed"));
}

避免使用基础类型与Optional

之前的Stream中,测试了装箱的性能损失,如IntegerStream与Stream<Integer>,性能损失的原因是大量的流元素进行装箱操作;

这里Optional内只放了一个值,不存在这种情况,不推荐使用基础类型的Optional(OptionalInt、OptionalLong、OptionalDouble)因为他们没有filter和flatMap方法;

小结:

  • Optional是为解决对象参数为空值的一种容器;
  • Optional的使用方法与Stream很像,包括map、filter、flatMap;
  • 在返回值(如getter方法)里面尝试使用Optional;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值