¿Puede una interfaz Java definirse de forma que sólo enumeraciones puede extenderlo?

mchen.ja:

No hay ninguna razón en particular que quiero hacer esto - Me pregunto si es posible. Si ayuda, he aquí una situación ficticia en la que se podría utilizar:

Imagine un tipo de Enumque se utiliza como una fuente de datos de sólo lectura, de tal manera que cada valor de la Enumcontiene contenido distinto. Los Enumimplementos Readable. Ahora, supongamos que queremos un método que lee todos los valores de la Enumen una sola memoria intermedia. Eso podría ser implementado como un método de utilidad estática en una clase de ayuda (ver más abajo).

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;
    }
}

Preferiblemente, ese método se declaró en una interfaz, pero que podría ser confuso, ya que no sería inmediatamente obvio que las clases no Enum no deben poner en práctica tal método. Idealmente, la interfaz puede definirse de tal manera que el compilador se aseguraría de que sólo fue implementado por subclases de Enum. Aquí está un ejemplo de lo que la interfaz podría parecerse a:

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

    int readAll(CharBuffer cb) throws IOException;
}

No creo que es posible hacer que el compilador de garantizar que ReadableEnumsólo se implementa por las subclases de Enum- ¿es correcto?

GotoFinal:

Java por defecto no soporta nada de eso, usted pregunta por qué no con enlace a la especificación, pero no hay ninguna razón especial para que, al igual que nadie decidió añadir estas características, se podría proponer usted mismo - pero entonces es probable que aprender que no creo que sea algo necesario y no añadir esto a la lengua.

Pero Java proporciona bastante potente opción de implementar esto por sí mismo: el procesamiento de anotación.
He creado proyecto simple experto en java 8 con la anotación:

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface EnumInterface {}

Y con procesador especial

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;
    }
}

Y registrarlo en el META-INF/services/javax.annotation.processing.Processorarchivo:

com.gotofinal.enuminterface.EnumInterfaceProcessor

Este código podría ser probablemente ha mejorado mucho, nunca he escrito antes de cualquier procesador de anotación. Pero cuando vamos a crear otro proyecto maven y declarar éste a su dependencia y escribir código como este:

@EnumInterface
interface TestInterface {}

enum TestEnum implements TestInterface {}

class TestClass implements TestInterface {}

No seremos capaces de compilar con el error:

Interfaz com.gotofinal.enuminterface.TestInterface no se puede utilizar en clase no enumeración: com.gotofinal.enuminterface.TestClass

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=311952&siteId=1
Recomendado
Clasificación