我没有这样做的特殊理由-我只是想知道是否有可能。如果有帮助,可以使用以下虚构情况:
想象一下,将其类型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;
}
}
Run Code Online (Sandbox Code Playgroud)
最好在接口中声明该方法,但这可能会造成混淆,因为非Enum类不应该立即实现这种方法并不是很明显。理想情况下,接口的定义方式应使编译器可以确保仅由的子类实现Enum。这是该接口可能看起来像的一个示例:
interface ReadableEnum extends Readable {
int read(CharBuffer cb) throws IOException;
int readAll(CharBuffer cb) throws IOException;
}
Run Code Online (Sandbox Code Playgroud)
我认为不可能使编译器确保ReadableEnum仅由的子类实现Enum-正确吗?
Got*_*nal 10
Java默认情况下不支持这样的功能,您问为什么不使用规范链接,但是没有特别的原因,为什么没有人决定添加这样的功能,您可以自己提出它-但是您可能会发现它们不要认为这是必需的,不会将其添加到语言中。
但是java提供了一个非常强大的选项来自己实现:注释处理。
我创建了带有注释的简单Java 8 Maven项目:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.CLASS)
public @interface EnumInterface {}
Run Code Online (Sandbox Code Playgroud)
并配有特殊处理器
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;
}
}
Run Code Online (Sandbox Code Playgroud)
并将其注册在META-INF/services/javax.annotation.processing.Processor文件中:
com.gotofinal.enuminterface.EnumInterfaceProcessor
Run Code Online (Sandbox Code Playgroud)
这段代码可能会改进很多,我之前从未写过任何注释处理器。但是,当我们创建另一个maven项目并将其声明为依赖项并编写如下代码时:
@EnumInterface
interface TestInterface {}
enum TestEnum implements TestInterface {}
class TestClass implements TestInterface {}
Run Code Online (Sandbox Code Playgroud)
我们将无法编译错误:
接口com.gotofinal.enuminterface.TestInterface不能用于非枚举类:com.gotofinal.enuminterface.TestClass
如果接口的所有实现都扩展了某个类,则该接口的所有实例也都是该类的实例;因此,该接口还必须扩展此类(了解有关is-a关系和子类型的更多信息)。
由于接口声明的extends子句中的每个类型必须是接口类型,因此您不能使接口扩展Enum 类 ; 因此,您不能阻止非枚举类实现您的接口。
你甚至不能替换实现它interface ReadableEnum extends Enum与abstract class ReadableEnum extends Enum自枚举类型不能声明抽象。
但是您仍然可以ReadableEnum通过使它扩展IEnum包含所有公共Enum方法的接口来使非枚举类的实现更加困难:
public interface IEnum<E extends Enum<E>> extends Comparable<E> {
String name();
int ordinal();
Class<E> getDeclaringClass();
}
Run Code Online (Sandbox Code Playgroud)
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);
}
}
Run Code Online (Sandbox Code Playgroud)
现在枚举可以实现ReadableEnum只通过实施read方法,而其他类必须实现name,ordinal,getDeclaringClass和compareTo也。
| 归档时间: |
|
| 查看次数: |
352 次 |
| 最近记录: |