可以定义Java接口,以便仅Enums可以扩展它吗? [英] Can a Java interface be defined such that only Enums can extend it?

查看:84
本文介绍了可以定义Java接口,以便仅Enums可以扩展它吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我没有特别的理由要这样做-我只是想知道是否有可能。如果有帮助,可以在一种虚构的情况下使用它:

There's no particular reason I want to do this - I'm just wondering if it is possible. If it helps, here's a fictional situation in which it could be used:

想象一下 Enum 的类型用作只读数据源,这样 Enum 的每个值都包含不同的内容。 枚举实现 Readable 。现在,假设我们需要一个将 Enum 的所有值读入单个缓冲区的方法。可以在帮助程序类中将其实现为静态实用程序方法(见下文)。

Imagine a type of Enum which is used as a read-only data source, such that each value of the Enum contains distinct content. The Enum implements Readable. Now, suppose we want a method that reads all values of the Enum into a single buffer. That could be implemented as a static utility method in a helper class (see below).

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 的子类实现。下面是该接口的示例:

Preferably, that method would be declared in an interface, but that could be confusing, since it wouldn't be immediately obvious that non-Enum classes should not implement such a method. Ideally, the interface could be defined in such a way that the compiler would ensure it was only implemented by subclasses of Enum. Here's an example of what that interface could possibly look like:

interface ReadableEnum extends Readable {
    int read(CharBuffer cb) throws IOException;

    int readAll(CharBuffer cb) throws IOException;
}

我认为不可能使编译器确保 ReadableEnum 仅由 Enum 的子类实现-正确吗?

I don't think it's possible to make the compiler ensure that ReadableEnum is only implemented by subclasses of Enum - is that correct?

推荐答案

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

Java by default does not support anything like that, you ask why not with link to specification, but there is no special reason why, just no one decided to add such feature, you could propose it yourself - but then you will probably learn that they don't think it's something needed and will not add this to the language.

但是Java提供了一个强大的选项来自己实现:注释处理。

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

But java provides pretty powerful option to implement this by yourself: annotation processing.
I've created simple java 8 maven project with annotation:

@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项目并将其声明为依赖项并编写如下代码时:

This code could be probably improved a lot, I've never wrote any annotation processor before. But when we will create another maven project and declare this one as dependency and write code like this:

@EnumInterface
interface TestInterface {}

enum TestEnum implements TestInterface {}

class TestClass implements TestInterface {}

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

We will not be able to compile it with error:


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

Interface com.gotofinal.enuminterface.TestInterface can't be used on non enum class: com.gotofinal.enuminterface.TestClass

这篇关于可以定义Java接口,以便仅Enums可以扩展它吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆