简体   繁体   English

java enum 类可以实现“Comparable”接口吗?

[英]Could java enum class implements "Comparable" interface?

Seems that enum class already has a final compareTo function, which cannnot be overriden.似乎枚举类已经有一个final的 compareTo 函数,它不能被覆盖。 But my requirement is to customize an enum class like this:但我的要求是像这样自定义一个枚举类:

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

Then I wish to have it implements Comparable interface to have the ability to compare among Add , Sub , Mul , etc. But seems I cannot use implements Comparable<Operator> as described above, javac gives compilation error:然后我希望它实现 Comparable 接口以便能够在AddSubMul等之间进行比较。但似乎我不能如上所述使用implements Comparable<Operator> ,javac给出了编译错误:

Redundant superinterface Comparable<Operator> for the type Operator, already defined by Enum<Operator>Java(16777547)

So how to achieve my goal?那么如何实现我的目标呢? Thanks.谢谢。

您始终可以声明Comparator.comparingInt(Operator::getPriority)并使用它来比较您的运算符。

Then I wish to have it implements Comparable interface to have the ability to compare among Add , Sub , Mul , etc然后我希望它实现Comparable接口以便能够在AddSubMul等之间进行比较

You can't change the implementation of Comparable in your enum .您不能在enum中更改Comparable的实现。

Every enum implicitly implements Comparable because its parent java.lang.Enum has implemented this interface and method compareTo() is marked with final modifier, ie you can't override it.每个枚举都隐式实现了Comparable ,因为它的父java.lang.Enum已经实现了这个接口并且方法compareTo()被标记为final修饰符,即你不能覆盖它。

Here is the quote from the Javadoc :这是来自Javadoc的引用:

public final int compareTo(E o)

Compares this enum with the specified object for order.将此枚举与指定的对象进行比较以进行排序。 Returns a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.返回负整数、零或正整数,因为此对象小于、等于或大于指定对象。 Enum constants are only comparable to other enum constants of the same enum type.枚举常量仅可与相同枚举类型的其他枚举常量进行比较。 The natural order implemented by this method is the order in which the constants are declared .此方法实现的自然顺序是声明常量的顺序。

You can define a Comparator implemented based on the priority property as public static field.您可以将基于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;
    }
}

If you need the order to reflect the standard order of arithmetic operations: Multiplication/Division comes before Addition/Subtraction , then the value of priority of the MUL and DIV needs to be lower than the value of priority of ADD and SUB .如果您需要顺序来反映算术运算的标准顺序:乘法/除法加法/减法之前,那么MULDIVpriority级值需要低于ADDSUBpriority值。

The names of enum-member Add , Sub , etc., as well as names of constructor parameters _op , _priority in your code are not aligned with the Java naming convention .您的代码中的枚举成员AddSub等名称以及构造函数参数_op_priority的名称与Java 命名约定不一致。

Ok: first, as you mentioned in your question, the enum implicitly inherits from java.lang.Enum which has a final compare method, so you cannot override it.好的:首先,正如您在问题中提到的,枚举隐式继承自 java.lang.Enum ,它具有最终比较方法,因此您不能覆盖它。 You could use a byte code manipulation Framework wo intercept the call, but that is more complicated that usefull.您可以使用字节码操作框架来拦截调用,但这更复杂但更有用。

So to work around that, just put your own compare function under a different name所以要解决这个问题,只需将您自己的比较函数放在不同的名称下

 public int comparePrio(Operator other) {
        return Integer.compare(this.priority, other.priority);
    }

This should serve your purpose in most cases.在大多数情况下,这应该可以满足您的目的。

If you really need an instance of Comparable, you can make use of the fact, that the Compairable Interface has only one method, so you can make one using a method-lambda like so:如果你真的需要一个 Comparable 的实例,你可以利用 Compairable 接口只有一个方法这一事实,所以你可以使用一个 method-lambda 来创建一个,如下所示:

public Comparable<Operator> asPriorityComparable(){
        return this::comparePrio;
    }

A small test shows that this is working as expected:一个小测试表明这是按预期工作的:

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

Produces output产生输出

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

Full enum class is完整的枚举类是

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

Why compare method is final为什么比较方法是最终的

As you noted correctly, you cannot override the compare function of the enum class, because it is final.正如您正确指出的那样,您不能覆盖枚举类的比较函数,因为它是最终的。

The method is final, because of the semantics of an enum (Enumerated).由于枚举(枚举)的语义,该方法是最终的。 It represent a number of values that could be completely substituted with there "ordnial" value.它代表了许多可以完全替换为“普通”值的值。 Actually, most applications of enum work with the ordinal value only.实际上,枚举的大多数应用程序仅使用序数值。 The names are just to make it easier to handle them.这些名称只是为了更容易处理它们。

Thus, the natural sort order (that is defined by the comparable interface) is to order by Ordinals and not on some field values.因此,自然排序顺序(由可比较接口定义)是按序数排序,而不是按某些字段值排序。 Actually, most enums don't have fields at all.实际上,大多数枚举根本没有字段。

So if you find your self in a spot where you need to order enums based on something other than there ordinals, you should probably reconsider using enums ...因此,如果您发现自己处于需要根据序数以外的其他内容订购枚举的地方,您可能应该重新考虑使用枚举...

What you could do:你可以做什么:

I'm going out an a limb here, since you didn't exactly specify what your final goal here is, but in most cases someone tries to implement Comparable, they try to sort a List or Array.我要在这里尝试一下,因为您没有确切说明您的最终目标是什么,但在大多数情况下,有人尝试实现 Comparable,他们尝试对 List 或 Array 进行排序。

If that is the case, you could just work around that issue using a Comparator.如果是这种情况,您可以使用比较器解决该问题。 Both the Array.sort, Collections.sort and Stream.sorted method have a overload that accepts a Comperator to define the sort order. Array.sort、Collections.sort 和 Stream.sorted 方法都有一个重载,它接受 Comperator 来定义排序顺序。

So what you could do is add a method to your enum like所以你可以做的是向你的枚举添加一个方法,比如

 public static int comparePrio(Operator first, Operator second) {
        return Integer.compare(first.priority, second.priority);
    }

and then call the sort method like this:然后像这样调用排序方法:

Collections.sort(myListOfValues, Operator::comparePrio);

(Code for Arrays and Streams is analog) (数组和流的代码是模拟的)

Warning: The code is not null save.警告:代码不是空保存。 If null is a possibility, you need to add additional null checks in your comparePrio method如果可能为 null,则需要在 comparePrio 方法中添加额外的 null 检查

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM