繁体   English   中英

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

[英]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 接口以便能够在AddSubMul等之间进行比较。但似乎我不能如上所述使用implements Comparable<Operator> ,javac给出了编译错误:

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

那么如何实现我的目标呢? 谢谢。

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

然后我希望它实现Comparable接口以便能够在AddSubMul等之间进行比较

您不能在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;
    }
}

如果您需要顺序来反映算术运算的标准顺序:乘法/除法加法/减法之前,那么MULDIVpriority级值需要低于ADDSUBpriority值。

您的代码中的枚举成员AddSub等名称以及构造函数参数_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.

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