1. 概述

在使用 Java 时,理解和操作对象类型是基础技能。一个常见的挑战是检查一个对象是否属于枚举类型(Enum)。

在这个快速教程中,我们将探讨各种方法和最佳实践,以确定一个对象的类型是否为枚举。

2. 问题介绍

枚举类型,或称为 enum,提供了一种强大方式,在特定类型内表示一组固定的值。动态确认一个对象是否为 enum 类型对于编写健壮且类型安全的代码至关重要。

例如,我们有一个简单的枚举:

enum Device {
    Keyboard, Monitor, Mouse, Printer
}

现在假设我们有一个对象:

Object obj = Device.Keyboard;

如图所示,obj 声明为 Object 类型。我们的挑战是检查它的类型是否为 enum。在本教程中,我们将学习不同的解决方法,并使用单元测试断言来验证结果。

3. 使用 instanceof 运算符

我们知道所有枚举都继承自同一个超类:java.lang.Enuminstanceof 运算符允许我们检查一个对象是否是某个特定类或接口的实例。

因此,我们可以利用这个运算符来检查对象是否为 Enum 类型:

Object obj = Device.Keyboard;
assertTrue(obj instanceof Enum);

4. 使用 Enum.class.isInstance() 方法

instanceof 运算符相同,Class.isInstance() 方法也执行同样的功能。 因为我们想要检查 Enum 类型,所以 Enum.class.isInstance(obj) 可以解决问题:

Object obj = Device.Keyboard;
assertTrue(Enum.class.isInstance(obj));

5. 使用 Enum.class.isAssignableFrom() 方法

类似于 Class.isInstance() 方法,Class.isAssignableFrom() 方法检查左侧的类是否与给定的 Class 参数相同或为其父类。 这两个方法有细微差别,但用于解决我们的问题时,它们没有区别:

Object obj = Device.Keyboard;
assertTrue(Enum.class.isAssignableFrom(obj.getClass()));

6. Class.isEnum() 方法

另一种确认一个类是否表示枚举的方式是通过使用 Class 类提供的 isEnum() 方法:

Object obj = Device.Keyboard;
assertTrue(obj.getClass().isEnum());

这种方法相当直接。然而,它可能无法检查某些枚举实例。 接下来,让我们更深入地了解 Class.isEnum() 的局限性。

7. Class.isEnum() 的局限性:带有体的枚举实例

我们知道枚举可以有自定义方法,而且枚举实例可以重写这些方法。接下来,看另一个枚举例子:

enum Weekday {
    Monday, Tuesday, Wednesday, Thursday, Friday,
    Saturday {
        @Override
        boolean isWeekend() {
            return true;
        }
    },
    Sunday {
        @Override
        boolean isWeekend() {
            return true;
        }
    };

    boolean isWeekend() {
        return false;
    }
}

如上所示,WeekdayisWeekend() 方法。默认情况下,方法返回 false。但是,SaturdaySunday 实例重写了这个方法,使其返回 true

现在,让我们用学到的方法来验证一些 Weekday 实例,看看它们能否报告正确的结果。首先,我们拿 Monday 试一试:

Object monday = Weekday.Monday;
assertTrue(monday instanceof Enum);
assertTrue(Enum.class.isInstance(monday));
assertTrue(Enum.class.isAssignableFrom(monday.getClass()));
assertTrue(monday.getClass().isEnum());

如果运行测试,四种方法都会按预期工作。接下来,我们拿 Sunday 作为输入,重复测试:

Object sunday = Weekday.Sunday;
assertTrue(sunday instanceof Enum);
assertTrue(Enum.class.isInstance(sunday));
assertTrue(Enum.class.isAssignableFrom(sunday.getClass()));
assertFalse(sunday.getClass().isEnum()); // <-- isEnum() check failed when Enum values with body

这次,Class.isEnum() 并未给出正确答案。接下来,让我们理解为什么会这样。

当枚举实例重写了一个方法,比如我们例子中的 SaturdaySunday,会创建一个匿名子类,作为枚举的子类。在这种情况下,调用 Sunday.getClass() 会返回匿名子类,而不是 Weekday 类。因此,对类调用 isEnum() 会得到 false

因此,我们不能将 Class.isEnum() 方法视为解决这个问题的可靠方案。

8. 总结

确定一个对象的类型是否为枚举对于编写灵活和健壮的 Java 代码至关重要。在本文中,我们探讨了进行检查的不同方法:

  • instanceof 运算符
  • Enum.class.isInstance() 方法
  • Enum.class.isAssignableFrom() 方法

此外,我们强调了使用枚举值的类 Class.isEnum() 检查的潜在问题,指出在处理枚举实例内的重写方法时,其可靠性会降低。所以,不应将其作为最终解决方案依赖。