You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

重写默认方法并新增抽象方法的函数式接口执行行为异常问题

代码与问题重现

@FunctionalInterface
interface StringConsumer extends Consumer<String> {
    @Override
    public default void accept(String s) {
        System.out.println(s.toUpperCase());
    }
    void print(String s);
}

public class Test {
    public static void main(String[] args) {
        StringConsumer stringConsumerImpl= s -> System.out.println(s.toLowerCase());
        List<String> list = Arrays.asList("Dr", "Mr", "Miss", "Mrs");
        list.forEach(stringConsumerImpl);
    }
}

输出:

DR
MR
MISS
MRS

问题:定义继承自Consumer<String>的@FunctionalInterface接口StringConsumer,将accept重写为默认方法(输出大写),同时声明抽象方法print(String s)。用Lambda实现print(输出小写)后,调用list.forEach(stringConsumerImpl)却执行了默认的accept方法,与预期不符;且删除print方法时main方法会出现编译错误。


问题原因分析

  1. forEach只调用Consumer的accept方法
    List.forEach的参数类型是Consumer<? super T>,你的StringConsumer实现了Consumer<String>,因此forEach会直接调用Consumer接口定义的accept方法——也就是你在StringConsumer中重写的默认实现(输出大写),和Lambda实现的print方法无关联,因为forEach不会调用print。

  2. 删除print后编译错误的本质
    @FunctionalInterface要求接口必须有且仅有一个抽象方法。删除print后,StringConsumer继承的Consumer抽象方法accept被你重写成了默认方法,此时接口中没有任何抽象方法,违反了函数式接口的定义,因此编译器报错。


解决方法

方案1:让accept成为抽象方法,用Lambda实现它

修改StringConsumer,去掉accept的默认实现,让它继承Consumer的抽象accept,Lambda直接实现accept逻辑:

@FunctionalInterface
interface StringConsumer extends Consumer<String> {
    // 保留继承自Consumer的抽象accept方法
}

public class Test {
    public static void main(String[] args) {
        StringConsumer stringConsumerImpl= s -> System.out.println(s.toLowerCase());
        List<String> list = Arrays.asList("Dr", "Mr", "Miss", "Mrs");
        list.forEach(stringConsumerImpl);
    }
}

此时forEach调用accept时,会执行Lambda中的小写输出逻辑。

方案2:自定义遍历,调用print方法

如果要保留print抽象方法,需手动遍历列表并调用print,而非依赖forEach:

@FunctionalInterface
interface StringConsumer extends Consumer<String> {
    @Override
    public default void accept(String s) {
        System.out.println(s.toUpperCase());
    }
    void print(String s);
}

public class Test {
    public static void main(String[] args) {
        StringConsumer stringConsumerImpl= s -> System.out.println(s.toLowerCase());
        List<String> list = Arrays.asList("Dr", "Mr", "Miss", "Mrs");
        // 手动遍历调用print方法
        for(String s : list) {
            stringConsumerImpl.print(s);
        }
    }
}

这样输出会符合预期的小写字符串。

内容的提问来源于stack exchange,提问作者Alper ARSLAN

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 01:52:13