问题描述
我没有特别的理由要这样做-我只是想知道是否有可能。如果有帮助,可以在一种虚构的情况下使用它:
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:
这篇关于可以定义Java接口,以便仅Enums可以扩展它吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!