为了账号安全,请及时绑定邮箱和手机立即绑定

Java 接口是否可以定义为只有 Enum 可以扩展它?

Java 接口是否可以定义为只有 Enum 可以扩展它?

炎炎设计 2023-11-10 16:39:27
我想这样做并没有什么特别的原因——我只是想知道这是否可能。如果有帮助的话,这是一个可以使用它的虚构情况:想象一种类型Enum用作只读数据源,这样每个值都Enum包含不同的内容。器物。Enum_ Readable现在,假设我们需要一个将 的所有值读取Enum到单个缓冲区中的方法。这可以作为辅助类中的静态实用方法来实现(见下文)。public class ReadableEnumUtils {    /** reads data from all enum values into the charbuffer */    public static <T extends Enum<T> & Readable> int readAll(Class<T> clazz, CharBuffer cb) throws IOException {        int total = 0;        for (T e : clazz.getEnumConstants()) {            int intermediate = e.read(cb);            if (intermediate < 0) {                throw new IllegalArgumentException("The enum value \'" + e.name() + "\' had no data to read.");            }            total += intermediate;        }        return total;    }}最好在接口中声明该方法,但这可能会造成混淆,因为非 Enum 类不应实现此类方法并不是立即显而易见的。理想情况下,接口的定义方式可以使编译器确保它仅由 的子类实现Enum。以下是该界面的示例:interface ReadableEnum extends Readable {    int read(CharBuffer cb) throws IOException;    int readAll(CharBuffer cb) throws IOException;}我认为不可能让编译器确保ReadableEnum仅由子类实现Enum- 这是正确的吗?
查看完整描述

2 回答

?
慕森卡

TA贡献1806条经验 获得超8个赞

Java 默认情况下不支持类似的功能,您会问为什么不提供规范链接,但没有特殊原因,只是没有人决定添加这样的功能,您可以自己提出 - 但随后您可能会了解到它们不认为这是需要的,并且不会将其添加到语言中。


但是java提供了非常强大的选项来自行实现这一点:注释处理。

我创建了带有注释的简单 java 8 maven 项目:


@Target(ElementType.TYPE)

@Retention(RetentionPolicy.CLASS)

public @interface EnumInterface {}

并配有特殊处理器


import javax.annotation.processing.*;

import javax.lang.model.SourceVersion;

import javax.lang.model.element.*;

import javax.lang.model.type.*;

import javax.lang.model.util.Types;

import javax.tools.Diagnostic;

import java.util.*;


@SupportedAnnotationTypes("com.gotofinal.enuminterface.EnumInterface")

@SupportedSourceVersion(SourceVersion.RELEASE_8)

public class EnumInterfaceProcessor extends AbstractProcessor {

    @Override

    public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {

        Messager messager = processingEnv.getMessager();

        Types typeUtils = processingEnv.getTypeUtils();


        // first we scan for all interfaces marked with this annotation

        List<TypeElement> enumOnlyInterfaces = new ArrayList<>();

        for (Element rootElement : roundEnv.getRootElements()) { // getRootElements should return all types being compiled

            if (! (rootElement instanceof TypeElement)) {

                continue;

            }

            TypeMirror typeMirror = rootElement.asType();

            // we check if this class have our annotation, we could also here check if this is an interface (by checking if it does not extend Object directly) and throw error otherwise

            if (rootElement.getAnnotation(EnumInterface.class) != null) {

                enumOnlyInterfaces.add((TypeElement) rootElement);

            }

        }


        // and now we scan for any non enum types that implement this interface

        for (Element rootElement : roundEnv.getRootElements()) {

            if (! (rootElement instanceof TypeElement)) {

                continue;

            }

            TypeElement type = findImplementedInterface(rootElement.asType(), enumOnlyInterfaces, typeUtils);

            if (type == null) {

                continue;

            }

            if (! (rootElement.asType() instanceof DeclaredType)) {

                continue;

            }


            // it's fine if it is an enum

            if (this.isEnum(rootElement.asType(), typeUtils)) {

                continue;

            }


            // and we print error to compiler

            messager.printMessage(Diagnostic.Kind.ERROR, "Interface " + type.getQualifiedName()

                                                                 + " can't be used on non enum class: " + ((TypeElement) rootElement).getQualifiedName());

        }

        return false;

    }


    public TypeElement findImplementedInterface(TypeMirror type, List<TypeElement> interfaces, Types types) {

        for (TypeElement anInterface : interfaces) {

            // types.isSubtype(typeA, typeA) would return true, so we need to add this equals check

            if (!anInterface.asType().equals(type) && types.isSubtype(type, anInterface.asType())) {

                return anInterface;

            }

        }

        return null;

    }


    // maybe there is better way to do this... but I just scan recursively for a subtype with java.lang.Enum name, so it's not perfect but should be enough.

    public boolean isEnum(TypeMirror type, Types types) {

        for (TypeMirror directSupertype : types.directSupertypes(type)) {

            TypeElement element = (TypeElement) ((DeclaredType) directSupertype).asElement();

            if (element.getQualifiedName().contentEquals("java.lang.Enum")) {

                return true;

            }

            if (isEnum(directSupertype, types)) {

                return true;

            }

        }

        return false;

    }

}

并将其注册到META-INF/services/javax.annotation.processing.Processor文件中:


com.gotofinal.enuminterface.EnumInterfaceProcessor

这段代码可能可以改进很多,我以前从未编写过任何注释处理器。但是当我们创建另一个 Maven 项目并将其声明为依赖项并编写如下代码时:


@EnumInterface

interface TestInterface {}


enum TestEnum implements TestInterface {}


class TestClass implements TestInterface {}

我们将无法编译它并出现错误:


接口 com.gotofinal.enuminterface.TestInterface 不能用于非枚举类:com.gotofinal.enuminterface.TestClass


查看完整回答
反对 回复 2023-11-10
?
胡子哥哥

TA贡献1825条经验 获得超6个赞

如果接口的所有实现都扩展某个类,则该接口的所有实例也是该类的实例;因此,此接口还必须扩展此类。

由于接口声明的 extends 子句中的每个类型都必须是接口类型,因此您不能使接口扩展Enum ;因此,您无法阻止非枚举类实现您的接口。

您甚至无法通过替换来实现它,interface ReadableEnum extends Enum因为abstract class ReadableEnum extends Enum枚举类型不得声明抽象


但是您仍然可以通过使其扩展由所有公共方法组成的接口来使其更难实现ReadableEnum非枚举类:IEnumEnum

public interface IEnum<E extends Enum<E>> extends Comparable<E> {

    String name();

    int ordinal();

    Class<E> getDeclaringClass();

}

interface ReadableEnum<E extends Enum<E> & ReadableEnum<E>> extends Readable, IEnum<E> {

    int read(CharBuffer cb) throws IOException;


    default int readAll(CharBuffer cb) throws IOException {

        return ReadableEnumUtils.readAll(getDeclaringClass(), cb);

    }

}

现在枚举可以ReadableEnum只通过实现read方法来实现,而其他类也必须实现name、ordinal、getDeclaringClass和compareTo。


查看完整回答
反对 回复 2023-11-10
  • 2 回答
  • 0 关注
  • 69 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信