发布于2026-05-23 阅读(0)
扫一扫,手机访问

想用密封类的 permits 关键字来精准控制包级私有类的继承?这里有个关键点需要先明确:permits 关键字本身并不能直接用于声明包级私有类作为其许可的子类。 原因在于,Ja va 语言规范白纸黑字写着:所有列在 permits 列表里的子类,对密封类本身必须是“可见”的。如果子类是包级私有的,并且和密封类不在同一个包里,编译器会直接抛出“class is not visible”的错误。所以,要实现“包内精准控制继承”这个目标,思路得换一换——核心在于调整可见性策略和包结构设计,而不是指望 permits 去直接引用一个对外部不可见的类。
这是Ja va语言定下的硬规矩,没有变通的余地:
- 密封类和它所有通过 permits 声明的子类,必须位于同一个编译单元上下文中(简单说,如果用了模块系统就得在同一个模块里;没用的,就必须在同一个包里)。
- 每一个被许可的子类,其声明至少得具备包级访问权限(也就是不加public修饰符)或更高的可见性。
- 结论很直接:一个真正的、定义在其他包里的包级私有类,是绝对不可能出现在 permits 列表里的。
那么,怎么才能达到“只允许本包内某些特定类继承,对外部完全屏蔽”的效果呢?正确的做法是:
- 把密封类和所有你打算允许继承的子类,统统放到同一个包里。
- 密封类本身声明为 package-private(就是不写 public),子类也保持包级私有。
- 然后,大大方方地用 sealed 和 permits 列出这些同包子类的简单类名(注意,不需要写包前缀)。
来看一个具体的例子:
// 文件:com.example.shape.Shape.ja va (没有public,是包私有抽象类)
sealed abstract class Shape permits Circle, Rectangle, Triangle { }
// 文件:com.example.shape.Circle.ja va
final class Circle extends Shape { ... }
// 文件:com.example.shape.Rectangle.ja va
sealed class Rectangle extends Shape permits Square { ... }
✅ 这样一来,外部包既无法import这些类,也无法继承Shape(因为Shape对外不可见)。
✅ 而在同一个包内,所有子类的继承关系都受到permits列表的精确约束,想新增一个子类?必须显式地把它加进许可列表,并且满足final、sealed或non-sealed的继承要求。
✅ 所有的检查都在编译期完成,任何非法的继承企图在编译阶段就会失败。
如果你的项目已经启用了Ja va模块系统,那么封装可以做得更彻底:
- 在module-info.ja va文件中,不要导出(do not export)包含密封类的那个包。
- 这样,即使其他模块依赖了当前模块,它们也无法访问该包内的任何类型。
- 此时,密封类及其许可的子类,实质上只对模块内部(具体来说,是同一个模块下的同一个包)可见。这种封装强度,已经接近“模块级私有”了。
这套组合拳,既严格遵守了permits对类可访问性的要求,又实现了比传统包私有更坚固的封装边界。
有人可能会问,为什么不直接用private类呢?答案是Ja va不允许:
- private类只能作为内部类存在,而permits子句只接受顶级类(top-level class)的名称。
- 即便你强行写上permits InnerClass,编译器也会毫不客气地提示“not a valid permitted subclass: inner classes are not allowed”。
- 所以,记住这个结论:permits列表里只允许出现public或package-private的顶级类,private和protected在这里都行不通。
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
售后无忧
立即购买>office旗舰店
正版软件
正版软件
正版软件
正版软件
正版软件
1
2
3
7
8