Lambda - 类库

本文总结、摘录自书籍《Java 8 函数式编程》
系列文章 GitHub地址

类库

基本类型

在 Java 中,有一些相伴的类型,比如 int 和 Integer—— 前者是基本类型,后者是装箱类型。基本类型内建在语言和运行环境中,是基本的程序构建模块;而装箱类型属于普通的 Java 类,只不过是对基本类型的一种封装。 Java 的泛型是基于对泛型参数类型的擦除。这就解释了为什么在 Java 中想要一个包含整型值的列表 List<int>是不可行的,泛型只接收对象类型。

麻烦的是,由于装箱类型是对象,因此在内存中存在额外开销。比如,整型在内存中占用 4 字节,整型对象却要占用 16 字节。这一情况在数组上更加严重,整型数组中的每个元素 只占用基本类型的内存,而整型对象数组中,每个元素都是内存中的一个指针,指向 Java 堆中的某个对象。在最坏的情况下,同样大小的数组,Integer[] 要比 int[] 多占用 6 倍内存。

将基本类型转换为装箱类型,称为装箱,反之拆箱。两者都需要额外的开销,对于需要大量数值运算的算法来说,装箱和拆箱的计算开销,以及装箱类型占用的额外内 存,会明显减缓程序的运行速度。为了减小这些性能开销,Stream 类的某些方法对基本类型和装箱类型做了区分。如mapTolong函数与其它类似函数即为该方面的一个尝试。在 Java 8 中,仅对整型、 长整型和双浮点型做了特殊处理,因为它们在数值计算中用得最多,特殊处理后的系统性 能提升效果最明显。

在有可能的情况下,我们应该尽量多的使用对基本类型做过特殊处理的方法,进而改善性能:

List<Integer> items = new ArrayList<>();
items.add(12);
items.add(13);

int[] array = items.stream().mapToInt(item -> item).toArray();

重载解析

在 Java 中可以重载方法,造成多个方法有相同的方法名,但签名确不一样。这在推断参数 类型时会带来问题,因为系统可能会推断出多种类型。这时,javac 会挑出最具体的类型。下面例子中执行的add方法为第一个。

public void add(String item) {...}

public void add(Object item) {...}
add("aaa");

Lambda 重载方法:

public interface BiFunction<T, R> {
    R test(T t);
}

public interface IntegerFunction extends BiFunction<Integer, Integer> {
    //
}
public void add (BiFunction<Integer, Integer> lambda) {...}

public void add (IntegerFunction lambda) {...}
add ((x) -> x + 1);

Java也会推到出最具体的函数接口类型,所以执行第二个方法。

模糊调用错误:

public interface BiFunction<T, R> {
    R test(T t);
}

public interface IntegerFunction {
    Integer test(Integer t);
}
add ((x) -> x + 1);

在类型匹配上,两个add方法都匹配,这回使得代码无法通过编译。

@FunctionalInterface

该注释会强制 javac 检查一个接口是否符合函数接口的标准。如果该注释添加给一个枚举类型、类或另一个注释,或者接口包含不止一个抽象方法,javac 就会报错。重构代码时, 使用它能很容易发现问题。

接口default关键字

我们在代码实现接口的实践中,若遇见接口增加方法定义时,会造成大量的实现类需要实现新增的方法,如Collection接口,若此接口新增一个方法,将造成大量的相关实现类必须实现此方法。Java 8 为我们解决了此问题–default关键字,使用此关键字修饰的接口方法可以书写代码块,表示方法的默认实现。形式如下:

public interface Say {
    default void say() {
        System.out.println("Hello");
    }
}
接口继承
public interface SayThank extends Say {
    @Override
    default void say() {
        System.out.println("Thanks");
    }
}

可以使用父接口的方法实现:

public interface SayThank extends Say {
    @Override
    default void say() {
        Say.super.say();
    }
}

关于多重实现

public interface A {
    default void action() {...}
}

public interface B {
    default void action() {...}
}

public class C implements A, B {
    //
}

上面的实现会造成编译错误,因为编译器不明确应该继承哪个接口的默认方法,解决方法就是实现这个方法,实现时可以指定使用接口中的默认实现:

public class C implements A, B {
    public void action() {
        A.super.action();
    }
}

接口的静态方法

Java 8 中添加的一个新的语言特性,旨在帮助编写类库的开发人员,但对于日常应用程序的开发人员也同样适用。人们在编程过程中积累了这样一条经验,那就是一个包含很多静态方法的类。有时,类是 一个放置工具方法的好地方,比如 Java 7 中引入的 Objects类,就包含了很多工具方法, 这些方法不是具体属于某个类的。 当然,如果一个方法有充分的语义原因和某个概念相关,那么就应该将该方法和相关的类 或接口放在一起,而不是放到另一个工具类中。这有助于更好地组织代码,阅读代码的人 也更容易找到相关方法。下面截取Stream接口中的静态方法of :

@SafeVarargs
@SuppressWarnings("varargs") // Creating a stream from an array is safe
public static<T> Stream<T> of(T... values) {
    return Arrays.stream(values);
}

Optional

reduce方法的一个重点尚未提及:reduce方法有两种形式,一种如前面出现的需要有一 个初始值,另一种变式则不需要有初始值。没有初始值的情况下,reduce的第一步使用 Stream中的前两个元素。有时,reduce操作不存在有意义的初始值,这样做就是有意义 的,此时,reduce方法返回一个 Optional对象。 Optional是为核心类库新设计的一个数据类型,用来替换 null值。人们对原有的 null值 有很多抱怨,甚至连发明这一概念的 Tony Hoare 也是如此,他曾说这是自己的一个“价值 连城的错误”。

人们常常使用 null值表示值不存在,Optional对象能更好地表达这个概念。使用 null代 表值不存在的最大问题在于 NullPointerException。一旦引用一个存储 null值的变量,程 序会立即崩溃。使用 Optional对象有两个目的:首先,Optional对象鼓励程序员适时检查 变量是否为空,以避免代码缺陷;其次,它将一个类的 API 中可能为空的值文档化,这比 阅读实现代码要简单很多。

Optional对象相当于值容器,可以通过get方法获取:

Optional<String> apple = Optional.of("apple");
apple.get();

Optional对象也可以为null,因此还有一个对应的工厂方法empty,另外一个工厂方法ofNullable则可以将null值转换成Optional对象。

Optional empty = Optional.empty();

Optional empty1 = Optional.ofNullable(null);

判断Optional是否为空值:

empty.isPresent();

使用Optional对象的方式之一是在调用 get()方法前,先使用 isPresent检查 Optional对象是否有值。使用 orElse方法则更简洁,当 Optional对象为空时,该方法提供了一个 备选值。如果计算备选值在计算上太过繁琐,即可使用 orElseGet方法。该方法接受一个 Supplier对象,只有在 Optional对象真正为空时才会调用。

public void testOptional() {
    Optional<Integer> optionalInteger = Optional.empty();
    System.out.println(optionalInteger.orElse(1));
    System.out.println(optionalInteger.orElseGet(() -> 1));

    Optional<Integer> optionalInteger1 = Optional.of(21);
    System.out.println(optionalInteger1.orElse(21));
    System.out.println(optionalInteger1.orElseGet(() -> 1));
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值