1

好的,在堆栈上的这个答案中,答案的海报显示了一个如何在枚举中使用抽象方法的示例。我将在这里为后代重复该答案,尽管稍作修改以更好地说明我的问题的基础。

考虑这个使用抽象方法的枚举:

public enum Vehicle {
    CAR {
        public String action() { return "DRIVE!"; }
    },
    TRUCK {
        public String action() { return "DRIVE!"; }
    },
    BUS {
        public String action() { return "DRIVE!"; }
    },
    TRACTOR {
        public String action() { return "DRIVE!"; }
    },
    MOTORCYCLE {
        public String action() { return "RIDE!"; }
    },
    BOAT {
        public String action() { return "SAIL!"; }
    },
    AIRPLANE {
        public String action() { return "PILOT!"; }
    };

    public abstract String action();
}

如您所见,由于“动作”是一个抽象函数,因此每个元素都必须通过枚举的匿名子类定义覆盖。

将此与这个无抽象、功能相同的版本进行对比,它甚至使用完全相同的 API:

enum Vehicle {
    CAR,
    TRUCK,
    BUS,
    TRACTOR,
    MOTORCYCLE,
    BOAT,
    AIRPLANE;

    public String action(){
        switch(this)
        {
            case MOTORCYCLE : return "RIDE!";
            case BOAT       : return "SAIL!";
            case AIRPLANE   : return "FLY!";
            default         : return "DRIVE!";
        }
    }
}

在此示例中,您只需指定与默认值不同的特定情况。此外,它将值保存在一个漂亮、清晰易读的列表中,并减少了大量无关代码。

也许我遗漏了一些东西,但是抽象方法方法是否有技术优势?非抽象版本没有给你什么?它有什么额外的能力吗?

注意:我怀疑实际的答案是因为它本身并不是枚举的函数,而是因为枚举被编译为一个类并且一个类支持抽象函数。

但是,我也不确定这是否正确,因为正如其他人所展示的那样,枚举编译为 astatic final class这意味着它不能被子类化。也许编译器在使用抽象函数时没有添加“final”。不确定,因为我无法查看生成的输出,所以我不能肯定,但这是有道理的。

但特别是对于这个问题,具有抽象函数的枚举可以做任何非抽象版本不能做的事情吗?

4

3 回答 3

3

问题不应该是一个人可以做些什么,因为如果编程正确,他们都可以以完全相同的方式行事。

这是关键部分。使用 switch 语句的版本在将来向枚举中添加新项时很容易编程错误。忘记为新项目指定一个案例将导致default使用可能不是您想要的。

我总是使用抽象方法版本,因为忘记覆盖action()是编译时错误,而忘记添加新case方法是运行时错误。

在编译时可以捕获的错误越多越好。

于 2017-07-26T17:14:00.727 回答
2

我在这里主要是同意 Andreas 的回答,但还要补充一点,Java 枚举允许您以多种不同的方式实现示例中描述的行为。

您的一个抱怨是,由于action()它是抽象的,您必须在每个枚举常量上实现它,并且您喜欢该switch策略允许您定义默认情况。这是使用默认情况的另一种方法:

enum Vehicle {

    CAR,
    TRUCK,
    BUS,
    TRACTOR,
    MOTORCYCLE {
        public String action() { return "RIDE!"; }
    },
    BOAT {
        public String action() { return "SAIL!"; }
    },
    AIRPLANE {
        public String action() { return "PILOT!"; }
    };

    public String action(){
        return "DRIVE!";
    }
}

您的另一个抱怨是所有样板代码都难以扫描枚举。如果你想减少样板文件,同时仍然获得每个枚举常量都有自己的“动作”的编译时强制,你可以这样做:

enum Vehicle {

    CAR        ("DRIVE!"),
    TRUCK      ("DRIVE!"),
    BUS        ("DRIVE!"),
    TRACTOR    ("DRIVE!"),
    MOTORCYCLE ("RIDE!"),
    BOAT       ("SAIL!"),
    AIRPLANE   ("PILOT!");

    private final String action;

    Vehicle(String action) {
        this.action = action;
    }

    public String action() {
        return action;
    }
}

就个人而言,我喜欢 Java 枚举的功能,以及它们支持的所有不同策略/技术。

于 2017-07-26T18:06:02.060 回答
1

具有抽象函数的 Java 枚举可以做什么,而没有使用它们的非抽象版本不能?

没有!

这不是代码能力的问题。这是一个方便、代码风格和代码保护的问题。

你不妨问:增强的 for 循环能做什么而普通的 for 循环不能?也没什么,但是迭代一个Iterable或一个数组比使用Iterator你自己方便得多。

正如您向自己展示的那样,您可以使用语句执行完全相同的操作switch,但是switch语句非常脆弱case,即在进行更改时它们很容易中断,因为在添加新值时很容易忘记添加额外的语句。

于 2017-07-26T17:35:27.517 回答