4.3 重载解析
在Java中可以重载方法,造成多个方法有相同的方法名,但签名确不一样。这在推断参数类型时会带来问题,因为系统可能会推断出多种类型。这时,javac会挑出最具体的类型。如例4-5中的方法调用在选择例4-6中定义的重载方法时,输出String,而不是Object。
例4-5 方法调用
overloadedMethod("abc");
例4-6 两个重载方法可供选择
private void overloadedMethod(Object o) {System.out.print("Object");}private void overloadedMethod(String s) {System.out.print("String");}
BinaryOperator是一种特殊的BiFunction类型,参数的类型和返回值的类型相同。比如,两个整数相加就是一个BinaryOperator。
Lambda表达式的类型就是对应的函数接口类型,因此,将Lambda表达式作为参数传递时,情况也依然如此。操作时可以重载一个方法,分别接受BinaryOperator 和该接口的一个子类作为参数。调用这些方法时,Java推导出的Lambda表达式的类型正是最具体的函数接口的类型。比如,例4-7在例4-8的两个方法中选择时,输出的是IntegerBinaryOperator。
例4-7 另外一个重载方法调用
overloadedMethod((x, y) -> x + y);
例4-8 两个重载方法可供选择
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");}
当然,同时存在多个重载方法时,哪个是“最具体的类型”可能并不明确。如例4-9所示。
例4-9 重载方法导致的编译错误
overloadedMethod((x) -> true);private interface IntPredicate {public boolean test(int value);}private void overloadedMethod(Predicate<Integer> predicate) {System.out.print("Predicate");}private void overloadedMethod(IntPredicate predicate) {System.out.print("IntPredicate");}
传入overloadedMethod方法的Lambda表达式和两个函数接口Predicate、IntPredicate在类型上都是匹配的。在这段代码块中,两种情况都定义了相应的重载方法,这时,javac就无法编译,在错误报告中显示Lambda表达式被模糊调用。IntPredicate没有继承Predicate,因此编译器无法推断出哪个类型更具体。
将Lambda表达式强制转换为IntPredicate或Predicate类型可以解决这个问题,至于转换为哪种类型则取决于要调用哪个函数接口。当然,如果以前你曾自行设计过类库,就可以将其视为“代码异味”,不该再重载,而应当开始重新命名重载方法。
总而言之,Lambda表达式作为参数时,其类型由它的目标类型推导得出,推导过程遵循如下规则:
- 如果只有一个可能的目标类型,由相应函数接口里的参数类型推导得出;
- 如果有多个可能的目标类型,由最具体的类型推导得出;
- 如果有多个可能的目标类型且最具体的类型不明确,则需人为指定类型。
