[英]Could java enum class implements "Comparable" interface?
似乎枚举类已经有一个final
的 compareTo 函数,它不能被覆盖。 但我的要求是像这样自定义一个枚举类:
enum Operator {
Add('+', 1),
Sub('-', 1),
Mul('*', 2),
Div('/', 2);
private char op;
private int priority;
private Operator(char _op, int _priority) {
op = _op;
priority = _priority;
}
}
然后我希望它实现 Comparable 接口以便能够在Add
、 Sub
、 Mul
等之间进行比较。但似乎我不能如上所述使用implements Comparable<Operator>
,javac给出了编译错误:
Redundant superinterface Comparable<Operator> for the type Operator, already defined by Enum<Operator>Java(16777547)
那么如何实现我的目标呢? 谢谢。
您始终可以声明Comparator.comparingInt(Operator::getPriority)
并使用它来比较您的运算符。
然后我希望它实现
Comparable
接口以便能够在Add
、Sub
、Mul
等之间进行比较
您不能在enum中更改Comparable
的实现。
每个枚举都隐式实现了Comparable
,因为它的父java.lang.Enum
已经实现了这个接口并且方法compareTo()
被标记为final
修饰符,即你不能覆盖它。
这是来自Javadoc的引用:
public final int compareTo(E o)
将此枚举与指定的对象进行比较以进行排序。 返回负整数、零或正整数,因为此对象小于、等于或大于指定对象。 枚举常量仅可与相同枚举类型的其他枚举常量进行比较。 此方法实现的自然顺序是声明常量的顺序。
您可以将基于priority
属性实现的Comparator
定义为public static
字段。
enum Operator {
MUL('*', 1),
DIV('/', 1),
ADD('+', 2),
SUB('-', 2);
public static final Comparator<Operator> BY_PRIORITY =
Comparator.comparing(Operator::getPriority);
private char op;
private int priority;
Operator(char op, int priority) {
this.op = op;
this.priority = priority;
}
public char getOp() {
return op;
}
public int getPriority() {
return priority;
}
}
如果您需要顺序来反映算术运算的标准顺序:乘法/除法在加法/减法之前,那么MUL
和DIV
的priority
级值需要低于ADD
和SUB
的priority
值。
您的代码中的枚举成员Add
、 Sub
等名称以及构造函数参数_op
、 _priority
的名称与Java 命名约定不一致。
好的:首先,正如您在问题中提到的,枚举隐式继承自 java.lang.Enum ,它具有最终比较方法,因此您不能覆盖它。 您可以使用字节码操作框架来拦截调用,但这更复杂但更有用。
所以要解决这个问题,只需将您自己的比较函数放在不同的名称下
public int comparePrio(Operator other) {
return Integer.compare(this.priority, other.priority);
}
在大多数情况下,这应该可以满足您的目的。
如果你真的需要一个 Comparable 的实例,你可以利用 Compairable 接口只有一个方法这一事实,所以你可以使用一个 method-lambda 来创建一个,如下所示:
public Comparable<Operator> asPriorityComparable(){
return this::comparePrio;
}
一个小测试表明这是按预期工作的:
for(var a : Operator.values()) {
for(var b : Operator.values()) {
System.out.printf("a=%-5s b=%-5s compair=%5d compairPrio=%5d%n", a.name(),b.name(),
a.compareTo(b),
a.asPriorityComparable().compareTo(b));
}
}
产生输出
a=Add b=Add compair= 0 compairPrio= 0
a=Add b=Sub compair= -1 compairPrio= 0
a=Add b=Mul compair= -2 compairPrio= -1
a=Add b=Div compair= -3 compairPrio= -1
a=Sub b=Add compair= 1 compairPrio= 0
a=Sub b=Sub compair= 0 compairPrio= 0
a=Sub b=Mul compair= -1 compairPrio= -1
a=Sub b=Div compair= -2 compairPrio= -1
a=Mul b=Add compair= 2 compairPrio= 1
a=Mul b=Sub compair= 1 compairPrio= 1
a=Mul b=Mul compair= 0 compairPrio= 0
a=Mul b=Div compair= -1 compairPrio= 0
a=Div b=Add compair= 3 compairPrio= 1
a=Div b=Sub compair= 2 compairPrio= 1
a=Div b=Mul compair= 1 compairPrio= 0
a=Div b=Div compair= 0 compairPrio= 0
完整的枚举类是
public enum Operator{
Add('+', 1),
Sub('-', 1),
Mul('*', 2),
Div('/', 2);
private char op;
private int priority;
private Operator(char _op, int _priority) {
op = _op;
priority = _priority;
}
public int comparePrio(Operator other) {
return Integer.compare(this.priority, other.priority);
}
public Comparable<Operator> asPriorityComparable(){
return this::comparePrio;
}
}
正如您正确指出的那样,您不能覆盖枚举类的比较函数,因为它是最终的。
由于枚举(枚举)的语义,该方法是最终的。 它代表了许多可以完全替换为“普通”值的值。 实际上,枚举的大多数应用程序仅使用序数值。 这些名称只是为了更容易处理它们。
因此,自然排序顺序(由可比较接口定义)是按序数排序,而不是按某些字段值排序。 实际上,大多数枚举根本没有字段。
因此,如果您发现自己处于需要根据序数以外的其他内容订购枚举的地方,您可能应该重新考虑使用枚举...
我要在这里尝试一下,因为您没有确切说明您的最终目标是什么,但在大多数情况下,有人尝试实现 Comparable,他们尝试对 List 或 Array 进行排序。
如果是这种情况,您可以使用比较器解决该问题。 Array.sort、Collections.sort 和 Stream.sorted 方法都有一个重载,它接受 Comperator 来定义排序顺序。
所以你可以做的是向你的枚举添加一个方法,比如
public static int comparePrio(Operator first, Operator second) {
return Integer.compare(first.priority, second.priority);
}
然后像这样调用排序方法:
Collections.sort(myListOfValues, Operator::comparePrio);
(数组和流的代码是模拟的)
警告:代码不是空保存。 如果可能为 null,则需要在 comparePrio 方法中添加额外的 null 检查
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.