覆盖接口默认方法

4

我很困扰以下代码无法正常运行:

接口:

public interface UOWProcessor {
  public default Integer countUOW(Object args) {
    return 1;
  }
}

实施:

public class ListUOWProcessor implements UOWProcessor {
private Integer total;

@Autowired
private UOWProcessor uowProcessor;

@Override
public Integer countUOW(List<?> args) {
    for (Object arg : args) {
        total += uowProcessor.countUOW(arg);
    }

    return total;
  }
}

编辑:添加第二个实现以澄清意图

public class MapUOWProcessor implements UOWProcessor {
    private Integer total;

    @Autowired
    private UOWProcessor uowProcessor;

    @Override
    public Integer countUOW(Map<?, ?> args) {
        for (Object item : args.values()) {
            total += uowProcessor.countUOW(item);
        }

        return total;
    }
}

问题在于 @Override 注释会出现错误,显示“该方法未覆盖其超类的任何方法”。这是 Java 11。请问正确的语法是什么?
以下是我尝试用英语表达的内容,如果上面不够清楚:
另一个类将调用接口方法 countUOW(类型未知的对象)。
如果存在该类型对象的接口实现(例如,List 的 ListUOWProcessor、Map 的 MapProcessor),则会调用实现并迭代返回总数(基本上是对象计数)。这将递归调用以考虑嵌套的数组/列表。
如果不存在实现,则可以假定传递的对象是某些非特定对象,然后将其计数为1。
对于原始方法调用,我们应返回传递的父对象中所有对象的计数,包括嵌套数组或映射中的对象。
我也可以从接口中删除“default”单词,并创建一个执行相同操作的 ObjectUOWProcessor - 但我担心它可能优先于其他所有实现,因为 List、Array、Map 都是 Object 的扩展。
编辑:我理解现在这不是 Override,而是 Overload。没有 @Override 注释的代码是否能实现所述功能?

1
尝试在您重写的函数中接收一个对象,并将其强制转换为List <?>。这将会给您一个警告,但如果您使用正确的参数调用它,它应该可以工作。 - J. Lengel
例子:List<?> list = (List<? extends Object>) arg0; - J. Lengel
仅仅使用 Integer countUOW(Object args) 不等同于 Integer countUOW(List<?> args) - user85421
2个回答

7
你很棒,包含了@Override注释。由于此操作,编译器告知你未正确重写countUOW方法。参数类型不同--接口方法采用Object,而实现类方法采用List<?>。这意味着你已经重载了该方法,而非重写。
你可以执行以下操作之一:
  1. Change the signature of the interface method to match.

    public default Integer countUOW(List<?> args) {
        return 1;
    }
    
  2. Change the signature of the implementing class method to match.

    @Override
    public Integer countUOW(Object args) {
        for (Object arg : (List<?>) args) {
            total += uowProcessor.countUOW(arg);
        }
    
        return total;
    }
    
  3. Make the interface generic and have the implementing class supply the type as a parameter.

    public interface UOWProcessor<T> {
        public default Integer countUOW(T args) {
            return 1;
        }
    }
    
    public class ListUOWProcessor implements UOWProcessor<List<?>> {
        // ...
        @Override
        public Integer countUOW(List<?> args) {
            for (Object arg : args) {
                total += uowProcessor.countUOW(arg);
            }
    
            return total;
        }
    }
    
请注意,这与您的方法是“default”没有任何关系。无论它是默认方法还是抽象方法,在接口、抽象类或具体类中重写的方法都会出现相同的错误,并有相同的选项来解决该问题。

如果移除 @Override 注解,并改用方法重载(而非覆盖),那么我是否可以实现问题中代码示例下方描述的功能? - 8t12c7081
1
不,覆盖方法的整个目的是多态性——根据对象的运行时类型在运行时选择方法的实现。重载无法实现这一点。如果您重载该方法并在类型为“UOWProcessor”的变量上调用它,则始终会得到“1”,因为实现类中的重载不会被调用。 - rgettman
好的,这里是一个后续问题。实现上面描述的选项3并包含一个类ObjectUOWProcessor implements UOWProcessor<Object>,是否允许我捕获传递的所有非特定对象,而不是为其编写特定处理器(List、Map)的对象,从而使这些特定对象像预期的那样工作? - 8t12c7081
是的,这样的类可以接受任何对象作为参数。它可以简单地继承默认方法,并返回1 - rgettman

1

你不能在实现中将一个 String 作为参数传递,因为它期望一个 List<?>,而接口明确表示所有类都应该能够作为参数传递,因为所有类都扩展自 Object。因此,你没有遵循接口中指定的标准。你改变了方法的签名,因此不是重写它,而是重载它。


我的意图是使用默认接口方法,传递一个字符串只会返回1。默认方法不是这样工作的吗? - 8t12c7081
default 关键字允许你在接口中实现方法,以防实现该接口的类不想覆盖它(或出于向后兼容的原因)。你误解了它。 - RaminS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接