Java的枚举值能不能用==比较?

(转自自己在其他地方写的,原文已废弃)

Java的枚举是一种很特殊的类,编译后实际上一个继承了java.lang.Enum的一个final类

如下面这个类:

enum E {
    A, B
}

编译后javap查看:

final class E extends java.lang.Enum<E> {
  public static final E A;
  public static final E B;
  public static E[] values();
  public static E valueOf(java.lang.String);
  static {};
}

其中每一个枚举对象都是以这个枚举类的单例形式存在,这种情况下,这个对象在整个JVM中的引用应当都是同一个,所以==比较应当是true。

例如采用如下代码进行测试

E a = E.A;
E b = E.valueOf("A");
E c = Enum.valueOf(E.class, "A");
E d = E.class.getEnumConstants()[0];
Object e = E.class.getField("A").get(null);
E f = E.values()[0];
System.out.println(a == b && a == c && a == d && a == e && a == f);

输出是true。


然而Java中的static final的引用并不等价于常数,枚举对象只是一个枚举类的实例,由类本身引用,类由ClassLoader引用,而现在Java世界里自定义ClassLoader已经是件稀松平常的事情了,那不同的ClassLoader中同样的枚举值应当有不同的引用,还能否用==比较?
我们自定义一个简单的ClassLoader

class MyCL extends ClassLoader {
    public Class<?> loadClass(String name) throws ClassNotFoundException {
        InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(name + ".class");
        if (resourceAsStream == null) {
            return super.loadClass(name);
        }
        byte[] def = new byte[10 * 1024];
        try {
            int length  = resourceAsStream.read(def);
            return defineClass(name, def, 0, length);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
}
ClassLoader classLoader1 = new MyCL();
ClassLoader classLoader2 = new MyCL();
Class clazz1 = classLoader1.loadClass("E");
Class clazz2 = classLoader2.loadClass("E");
System.out.println(clazz1 == clazz2);
Object c10 = clazz1.getEnumConstants()[0];
Object c20 = clazz2.getEnumConstants()[0];
System.out.println(c10); // 输出A
System.out.println(c20); // 输出A
System.out.println(c10 == c20); // 输出false

和预计的一样,此时==无法判断这两个枚举值是否相等


那如何才能比较呢?
我们知道对于Java对象都建议自己重写equals方法来比较,那是不是用equals就行了呢?

enum E {
    A, B;

    public boolean equals(Object o) {
        if (o != null && o instanceof E) {
            return this.ordinal() == ((E) o).ordinal();
        }
        return false;
    }
}

然而很遗憾,这是无法通过编译的,因为Enum已经覆盖了equals方法,而且还是final的。那Enum“自带”的equals是否能满足我们的要求呢?

System.out.println(c10.equals(c20)); // 输出false

很遗憾,还是false,阅读Enum的源代码就能明白,它就是用==实现的。
幸运的是,我们还知道,Enum有一个ordinal()方法,确保其值是等于枚举值的书写顺序的,而且我们还知道枚举值的toString()将返回枚举值的字面值。
像这样:

Enum a10 = (Enum) c10;
Enum a20 = (Enum) c20;
System.out.println(a10.ordinal() == a20.ordinal()); // 输出true
System.out.println(String.valueOf(c10).equals(String.valueOf(c20))); // 输出true

等等,好像还有一个问题,我们是不是应该先判断他们是同一个(枚举)类的实例?显然两个不同的枚举类都可以有一个叫A的枚举值,而且他们可能都在第一个位置。

System.out.println(c10.getClass().equals(c20.getClass())); // 输出false
System.out.println(c10 instanceof E); // 输出false
System.out.println(c10.getClass().isInstance(c20)); // 输出false
System.out.println(c10.getClass().getName().equals(c20.getClass().getName())); // 输出true

done.

再等一下,枚举类是final的,所以我们平时比较的时候不都是把他们写成这样:

E e10 = (E) c10; // 异常
E e20 = (E) c20;
System.out.println(e10 == e20);

再比较的吗?
而且他们不是在类型转换那步不就跪了吗?哪还轮得到比较?

所以担心==对不同的ClassLoader的值比较会失效的问题,是不是想多了?

好吧,确实是

这个问题纯属想多了!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值