4.3 重载解析

在Java中可以重载方法,造成多个方法有相同的方法名,但签名确不一样。这在推断参数类型时会带来问题,因为系统可能会推断出多种类型。这时,javac会挑出最具体的类型。如例4-5中的方法调用在选择例4-6中定义的重载方法时,输出String,而不是Object

例4-5 方法调用

  1. overloadedMethod("abc");

例4-6 两个重载方法可供选择

  1. private void overloadedMethod(Object o) {
  2. System.out.print("Object");
  3. }
  4. private void overloadedMethod(String s) {
  5. System.out.print("String");
  6. }

BinaryOperator是一种特殊的BiFunction类型,参数的类型和返回值的类型相同。比如,两个整数相加就是一个BinaryOperator

Lambda表达式的类型就是对应的函数接口类型,因此,将Lambda表达式作为参数传递时,情况也依然如此。操作时可以重载一个方法,分别接受BinaryOperator 和该接口的一个子类作为参数。调用这些方法时,Java推导出的Lambda表达式的类型正是最具体的函数接口的类型。比如,例4-7在例4-8的两个方法中选择时,输出的是IntegerBinaryOperator

例4-7 另外一个重载方法调用

  1. overloadedMethod((x, y) -> x + y);

例4-8 两个重载方法可供选择

  1. private interface IntegerBiFunction extends BinaryOperator<Integer> {
  2. }
  3. private void overloadedMethod(BinaryOperator<Integer> Lambda) {
  4. System.out.print("BinaryOperator");
  5. }
  6. private void overloadedMethod(IntegerBiFunction Lambda) {
  7. System.out.print("IntegerBinaryOperator");
  8. }

当然,同时存在多个重载方法时,哪个是“最具体的类型”可能并不明确。如例4-9所示。

例4-9 重载方法导致的编译错误

  1. overloadedMethod((x) -> true);
  2. private interface IntPredicate {
  3. public boolean test(int value);
  4. }
  5. private void overloadedMethod(Predicate<Integer> predicate) {
  6. System.out.print("Predicate");
  7. }
  8. private void overloadedMethod(IntPredicate predicate) {
  9. System.out.print("IntPredicate");
  10. }

传入overloadedMethod方法的Lambda表达式和两个函数接口PredicateIntPredicate在类型上都是匹配的。在这段代码块中,两种情况都定义了相应的重载方法,这时,javac就无法编译,在错误报告中显示Lambda表达式被模糊调用。IntPredicate没有继承Predicate,因此编译器无法推断出哪个类型更具体。

将Lambda表达式强制转换为IntPredicatePredicate类型可以解决这个问题,至于转换为哪种类型则取决于要调用哪个函数接口。当然,如果以前你曾自行设计过类库,就可以将其视为“代码异味”,不该再重载,而应当开始重新命名重载方法。

总而言之,Lambda表达式作为参数时,其类型由它的目标类型推导得出,推导过程遵循如下规则:

  • 如果只有一个可能的目标类型,由相应函数接口里的参数类型推导得出;
  • 如果有多个可能的目标类型,由最具体的类型推导得出;
  • 如果有多个可能的目标类型且最具体的类型不明确,则需人为指定类型。