基本类型和装箱类型的转换
由于Java的泛型都是基于对泛型类型的擦除,所以泛型的参数只能是基本类型对应的装箱类型。
由于装箱类型是对象,因此在内存中存在额外开销。在Java 8 中,仅对 整型 长整型 双浮点型 做了特殊处理
在Stream 类的某些方法对基本类型和装箱类型做了区分
对基本类型做特殊处理的方法在命名上有明确的规范。如果方法返回类型为基本类型,则在基本类型前加To,如ToLongFunction。
如果参数是基本类型,则不加前缀只需类型名即可
如果高阶函数使用基本类型,则在操作后加后缀To 再加基本类型,如mapToLong
这些基本类型都有与之对应的Stream,以基本类型名为前缀,如LongStream。事实上,mapToLong 方法返回的不是一个一般的Stream,而是一个特殊处理的Stream。在这个特殊的Stream 中,map 方法的实现方式也不同,它接受一个LongUnaryOperator 函数,将一个长整型值映射成另一个长整型值
通过一些高阶函数装箱方法,如mapToObj,也可以从一个基本类型的Stream 得到一个装箱后的Stream,如Stream<Long>。
exp:
public class BaseTypeTest {
//T->long
private ToLongFunction<String> toLongFunction;
//long->T
private LongFunction<String> longFunction;
@Before
public void setUp() throws Exception {
toLongFunction = new ToLongFunction<String>() {
@Override
public long applyAsLong(String value) {
return value.length();
}
};
}
@Test
public void test() throws Exception {
List<String>strings = Arrays.asList("123434","2312324","32323");
LongSummaryStatistics longSummaryStatistics = strings.stream().mapToLong(toLongFunction).summaryStatistics();
System.out.printf("Max: %d, Min: %d, Ave: %f, Sum: %d",
longSummaryStatistics.getMax(),
longSummaryStatistics.getMin(),
longSummaryStatistics.getAverage(),
longSummaryStatistics.getSum());
}
输出:
Max: 7, Min: 5, Ave: 6.000000, Sum: 18
重载解析
private interface IntegerBiFunction extends BinaryOperator<Integer> {
}
private void overloadedMethod(BinaryOperator<Integer> Lambda) {
System.out.print("BinaryOperator");
}
private void overloadedMethod(IntegerBiFunction Lambda) {
System.out.print("IntegerBinaryOperator");
}
同时存在多个重载方法时,哪个是“最具体的类型”可能并不明确
Lambda 表达式作为参数时,其类型由它的目标类型推导得出,推导过程遵循如下规则:
- 如果只有一个可能的目标类型,由相应函数接口里的参数类型推导得出;
- 如果有多个可能的目标类型,由最具体的类型推导得出;
- 如果有多个可能的目标类型且最具体的类型不明确,则需人为指定类型。
默认方法
使用default定义接口默认方法
public interface Parent {
public void message(String body);
public default void welcome() {
message("Parent: Hi!");
}
public String getLastMessage();
}
}
ParentImpl类仅仅实现的Parent接口,没有实现welcome 方法
@Test
public void parentDefaultUsed() {
Parent parent = new ParentImpl();
parent.welcome();
assertEquals("Parent: Hi!", parent.getLastMessage());
}
断言是正确的
默认方法的三定律
- 类胜于接口。
如果在继承链中有方法体或抽象的方法声明,那么就可以忽略接口中定义的方法。 - 子类胜于父类。
如果一个接口继承了另一个接口,且两个接口都定义了一个默认方法,
那么子类中定义的方法胜出。 - 没有规则三。
如果上面两条规则不适用,子类要么需要实现该方法,要么将该方法声明
为抽象方法。
Optional
Optional 是为核心类库新设计的一个数据类型,用来替换null 值。
- Optional.of(..)获取Optional对象
- Optional.empty 返回空的对象
- Optional.ofNullable 将空值转换成Optional对象
- isPresent 表示Optional对象是否有值
- orElse 当Optional 对象为空时,该方法提供了一个备选值
- orElseGet 该方法接受一个Supplier对象,只有在Optional对象真正为空时才会调用。
@Test
public void testOptional() throws Exception {
//1.Optional.of
Optional<String> s = Optional.of("a");
assertEquals("a", s.get());
//2.Optional.empty 返回空的对象
//3.Optional.ofNullable 将空值转换成Optional对象
//4.isPresent 表示Optional对象是否有值
Optional<String> empty = Optional.empty();
Optional<String> alsoEmpty = Optional.ofNullable(null);
assertFalse(empty.isPresent());
assertFalse(alsoEmpty.isPresent());
assertTrue(s.isPresent());
//5.orElse 当Optional 对象为空时,该方法提供了一个备选值
//6.orElseGet 该方法接受一个Supplier对象,只有在Optional对象真正为空时才会调用。
assertEquals("b", alsoEmpty.orElse("b"));
assertEquals("c", alsoEmpty.orElseGet(() -> "c"));
}