如何确保接口的未来实现也将扩展特定的类

发布于 2025-01-13 00:51:47 字数 334 浏览 5 评论 0原文

我有一个抽象类和两个扩展它的最终类

抽象类也是接口的实现。

现在,我必须删除两个子类之一并添加一个接口,以便人们仍然可以使用自己的实现。

但我需要确保实现新接口的人都会扩展现有抽象类。这是必需的,因为否则它将无法运行。

有没有办法实现这一点,或者我只能记录实施要求?

I have an abstract class and two final classes that extend it.

The abstract class is also an implementation of an interface.

Now I have to remove one of the two child classes and add an interface so that people can still come in with their own implementations.

But I need to ensure that whoever implements the new interface is going to extend the existing abstract class. It is required because otherwise it won't be functioning.

Is there a way to achieve that, or I can only document an implementation requirement?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

水中月 2025-01-20 00:51:47

据我了解,您的目标是通过在 API 中提供单点扩展来施加限制,同时公开您的 >顶级抽象给最终用户,以便允许他们在代码中使用这些抽象。

您可以通过 Java 17 引入的密封类和接口实现对类层次结构的可访问性和控制。

已密封

该功能由 JEP 409 最终确定。

密封接口只能通过
这些类和接口允许这样做。

即,如果将sealed修饰符应用于/接口,则意味着只有接口其声明的许可子句中列出的内容允许扩展/实施类/接口

permits子句需要始终出现在密封/接口的声明中嗯>。对于其许可子句中提到的所有子类/子接口,它是强制扩展/实施直接父级密封

反过来,每个子类/子接口都必须声明:

  • 密封(具有自己的许可条款等);
  • 非密封 - 一个修饰符,“打破密封”并将此子级转变为普通的/接口,可以不受任何限制地扩展;
  • 决赛(仅适用于课程)。

请注意,sealednon-sealed 修饰符都可以与 abstract 修饰符配合使用。这对于这项任务特别有用。

密封或非密封的类可能是抽象,并且具有
抽象成员。密封类可以允许子类,这些子类是
摘要

实现

这一想法是创建接口(现有的且需要由最终用户实现的接口)和现有抽象类 strong> sealed 对它们的扩展施加限制。

现有的最终类别将保持为最终

扩展点将由非密封抽象类表示,它扩展了现有抽象类并实现定义用户特定行为契约的接口

输入图片此处描述

通过这种方法,您可以防止 Base 接口Root< 定义的关键行为 /code> 类被实现在这是不受欢迎的方式,即使用 final 修饰符在 OpenChild class 中标记这些方法。同时为用户特定的行为提供一个契约,所有自定义类都应遵守该契约。

这就是它在代码中的样子:

public sealed interface Base permits Root {
    // critical behaviour
    void m1();
    void m2();
}

public sealed abstract class Root implements Base
                            permits ClosedChild, OpenChild {
    // critical behaviour
    public void m1() {} // default implementation
}

public final class ClosedChild extends Root { // can't be extended further
    // critical behaviour
    public void m2() { /* custom implementation */ }
}

public sealed interface Outer {
    // end-user-specific behaviour
    void m3();
    void m4();
}

public non-sealed abstract class OpenChild extends Root implements Outer {
    // critical behaviour
    final public void m1() { super.m1(); }                 // locked
    final public void m2() { /* custom implementation */ } // locked

    // has to be implemented by the end user
    public abstract void m5(); // + methods declared by Outer
}

替代方案

如果您使用的是早期版本的Java,因此无法利用密封功能,那么不幸的是,您需要制作< em>在控制顶级抽象扩展的能力和最终用户可访问这些抽象的能力之间进行选择。

作为部分解决方案,您可以向最终用户隐藏除了现有的最终类ClosedChild扩展点之外的所有内容强> OpenChild

为此,上面提到的所有接口都必须驻留在单独的包中。 OpenChildClosedChild 必须标记为 public接口 外部抽象类 必须是< strong>package private,即它们对OpenChildClosedChild可见,但它们的行为可以从任何地方访问。

注意:抽象类中声明的方法必须是public,接口中的所有方法默认都是public的,甚至接口本身也是私有的。

JDK 中这种方法的一个示例是包私有抽象类 AbstractStringBuilder,它不可公开访问。它的 public final 子类 StringBuilderStringBuffer 共享其行为。

As I understand, your goal is to impose restrictions by providing a single point of extension in your API and at the same time expose your top-level abstractions to end-users in order to allow them to utilize these abstractions in the code.

You can achieve both accessibility and control over the class hierarchy with sealed classes and interfaces introduced with Java 17.

Sealed

The feature was finalized by JEP 409.

A sealed class or interface can be extended or implemented only by
those classes and interfaces permitted to do so.

I.e. if sealed modifier is being applied to a class/interface it implies that only classes or interfaces listed in the permits clause of its declaration are allowed to extend/implement this class/interface.

The permits clause needs to be always present in the declaration of a sealed class/interface. And for all subclasses/subinterfaces mentioned in the permits clause of their parent it's mandatory to extend/implement the sealed the parent directly.

In turn, every subclass/subinterface has to be declared either:

  • sealed (with it own permits clause etc.);
  • non-sealed - a modifier that "breaks the seal" and turns this child into a normal class/interface which can be extended without any restrictions;
  • final (only for classes).

Note that both sealed and non-sealed modifiers play well with abstract modifier. Which is particularly useful for this task.

A class which is sealed or non-sealed may be abstract, and have
abstract members. A sealed class may permit subclasses which are
abstract
.

Implementation

The idea is to make both interfaces (existing and the one that needs to be implemented by the end-user) and existing abstract class sealed to impose restrictions on their extension.

Existing final class will remain to be final.

The extension point will be represented by a non-sealed abstract class, which extends the existing abstract class and implements the interface that defines a contract for user-specific behavior.

enter image description here

With this approach, you can prevent the critical behaviour defined by the Base interface and Root class from being implemented in an undesired way, by marking these methods inside the OpenChild class with final modifier. And at the same time provide a contract for user-specific behavior which all custom classes are expected to conform to.

That how it might look in the code:

public sealed interface Base permits Root {
    // critical behaviour
    void m1();
    void m2();
}

public sealed abstract class Root implements Base
                            permits ClosedChild, OpenChild {
    // critical behaviour
    public void m1() {} // default implementation
}

public final class ClosedChild extends Root { // can't be extended further
    // critical behaviour
    public void m2() { /* custom implementation */ }
}

public sealed interface Outer {
    // end-user-specific behaviour
    void m3();
    void m4();
}

public non-sealed abstract class OpenChild extends Root implements Outer {
    // critical behaviour
    final public void m1() { super.m1(); }                 // locked
    final public void m2() { /* custom implementation */ } // locked

    // has to be implemented by the end user
    public abstract void m5(); // + methods declared by Outer
}

Alternatives

If you are using an earlier version of Java and therefore can't utilize sealed feature, then unfortunately you need to make a choice between the ability to control the extension of your top-level abstractions and accessibility of these abstractions to the end-users.

As a partial solution, you can hide from the end-user everything apart from the existing final class ClosedChild and extension point OpenChild.

In order to do that, all the classes and interfaces mentioned above have to reside in a separate package. Classes OpenChild and ClosedChild must be marked as public. Interfaces Base, Outer and abstract class Root have to be package private, i.e. they will be visible to OpenChild and ClosedChild, but their behaviour will accessible from everywhere.

Note: methods declared inside the Base abstract class in order to make them assemble to the end-user must be public, all methods in interfaces are public by default even the interface itself package private.

An example of this approach in the JDK is a package private abstract class AbstractStringBuilder, which is not publicly accessible. And its public final subclasses StringBuilder and StringBuffer share its behaviour.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文