要用Java密封接口真正拦住外部框架非法实现,必须严丝合缝配齐修饰符、模块声明、permits列表和子类封闭策略:permits须显式列出所有合法实现类(拼写、大小写、包路径完全一致,不支持通配符或匿名类);每个实现类必须标注final、sealed或non-sealed;模块需双向声明exports/requires确保可见性;配合switch穷尽性检查强化契约保障。

要用Java密封接口(Sealed Interfaces)真正拦住外部框架的非法实现,关键不是加个sealed就完事,而是把修饰符、模块声明、permits列表和子类修饰三者严丝合缝地配齐。只做一半,第三方照样能绕过。
必须显式列出所有合法实现类
密封接口不接受“默认开放”或“隐式允许”。你得在permits里白纸黑字写全每一个被信任的实现类,一个都不能少,拼写、大小写、包路径都得完全一致:
- 不允许省略:哪怕只漏一个类名,编译直接失败,JVM不会帮你猜
-
不允许通配符或正则:不能写
permits *.impl.*这类表达式 - 不允许内部类或匿名类:permits后只能是顶层public类,且必须已声明
每个实现类必须带明确封闭策略
被permits列出的类,必须用final、sealed或non-sealed三者之一标注,缺一不可:
-
final class Success implements MyResult→ 终止链,最安全,适合数据载体 -
sealed class ValidationError implements MyResult permits MissingFieldError, InvalidFormatError→ 可控延续,适合分层错误建模 -
non-sealed class CustomError implements MyResult→ 主动放行,但仅限你亲自设计的扩展点,慎用
没加这三者任意一个?javac立刻报错:class X must be declared final, sealed, or non-sealed。
立即学习“Java免费学习笔记(深入)”;
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
模块边界必须双向打通
光同包还不够——模块化项目中,JVM会校验类加载时的模块可见性。非法实现常发生在第三方模块里,所以得靠module-info.java堵死:
- 在你的框架模块中:
exports com.example.core to com.example.core.impl(只导出给可信实现模块) - 在实现模块中:
requires com.example.core(声明依赖) - 确保
permits里所有类都在你exports的包下,且不在第三方模块中定义
否则即使代码写对了,也会在编译期报class X is not allowed to extend sealed interface Y。
配合模式匹配强化契约保障
密封接口真正的威力,在于让switch表达式具备穷尽性检查能力:
- 当所有
permits子类都是final或收敛的sealed时,switch (result)可省略default分支 - 一旦某子类误标为
non-sealed,编译器就会警告:switch expression does not cover all possible values - 这个检查是编译期强制的,反射、动态代理、字节码注入都无法绕过
它倒逼你把扩展意图写进类型系统,而不是藏在文档或约定里。

















