Enum 奇怪的泛型 自限定泛型 循环泛型

作者:wenyinfeng

转载时,请注明原文出处,谢谢!

public abstract class Enum<E extends Enum<E>>
extends Object
implements Comparable<E>, Serializable

为什么 Enum 类需要使用如下这么奇怪的泛型,<E extends Enum<E>>,初次看真是让我晕了, E 到底代表什么啊?为什么不定义成 public abstract class Enum extends Object 呢?很容易找到答案,Enum 需要实现Comparable 比较接口。

那似乎也可以定义成 public abstract class Enum<E>extends Object implements Comparable<E>, Serializable 。仔细想,就会发现会有问题,Comparable 的compareTo()方法是这样的: public final int compareTo(E o),我们肯定不希望这个 E 是任何类型的,而是希望这个E 是 Enum 类型的,同时我们不希望两个不同的 Enum 子类实例可以进行比较(没有意义),即我们最希望的是同一个 Enum 子类的实例进行比较。
 
下面 的 EnumClass.java 摘自 《Thinking in java 》第19章枚举类型,稍微修改了下。通过 javap javap Shrubbery 反编译,可以看到 Shrubbery 是一个继承自 Enum 的类。

Compiled from "EnumClass.java"
final class enumerated.Shrubbery extends java.lang.Enum{
    public static final enumerated.Shrubbery GROUND;
    public static final enumerated.Shrubbery CRAWLING;
    public static final enumerated.Shrubbery HANGING;
    static {};
    public static enumerated.Shrubbery[] values();
    public static enumerated.Shrubbery valueOf(java.lang.String);
}

由于擦除的原因, enum Shrubbery {} 实事上等价于 final class Shrubbery extends Enum<Shrubbery>(即告诉编译器Shrubbery 继承于 Enum,而这个Enum 的泛型参数就是 Shrubbery 自身)。现在我们检查下,Shrubbery 符不符合 <E extends Enum<E>> 形式,如果你还看不明白,可以把 E 替换成Shrubbery ,你可以看到这就是 Shrubbery 的类定义,所以Shrubbery 完全符合要求!另外编译加上 final 修饰的原因是不希望 Enum 的子类可以作为父类被其它类继承。
 
//: enumerated/EnumClass.java
// Capabilities of the Enum class
package enumerated;
import static net.mindview.util.Print.*;
 
enum Shrubbery { GROUND, CRAWLING, HANGING }
 
// 错误无法继承
//enum ShrubberySub extends Shrubbery {};
enum AnotherShrubbery{GROUND, CRAWLING, HANGING }
 
public class EnumClass {
  public static void main(String[] args) {
    for(Shrubbery s : Shrubbery.values()) {
      print(s + " ordinal: " + s.ordinal());
      printnb(s.compareTo(Shrubbery.CRAWLING) + " ");
      printnb(s.equals(Shrubbery.CRAWLING) + " ");
      print(s == Shrubbery.CRAWLING);
      print(s.getDeclaringClass());
      print(s.name());
      print("----------------------");
    }
    // Produce an enum value from a string name:
    for(String s : "HANGING CRAWLING GROUND".split(" ")) {
      Shrubbery shrub = Enum.valueOf(Shrubbery.class, s);
      print(shrub);
    }
   
    // 编译不过,在不同 enum 类型中进行比较
    Shrubbery.GROUND.compareTo(AnotherShrubbery.GROUND);
  }
}
 
为了验证上面的推出的结果,我写了下面手机类来进行测试。同类手机可以进行系统版本号的比较,不同类的手机系统版本号比较没有意义。通过 E extends Handphone<E> 的泛型限定,我们就可以阻止编译器在 Iphone 和 Android 手机之间进行系统版本号的比较。

//: generics/WeirdGenerics.java
// 怪异的泛型
package generics;

abstract class Handphone<E extends Handphone<E>> implements Comparable<E>{
    public abstract int osVersion();
    public int compareTo(E o){
        return osVersion() - o.osVersion();
    }
}

class Iphone extends Handphone<Iphone>{
    private int version;
    public Iphone(int version){
        this.version = version;
    }
    public int osVersion(){
        return version;
    }
}

class Android extends Handphone<Android>{
    private int version;
    public Android(int version){
        this.version = version;
    }
    public int osVersion(){
        return version;
    }
}

public class WeirdGenerics{
    public static void main(String []args){
        Iphone i4 = new Iphone(4), i5 = new Iphone(5);
        Android a2 = new Android(2), a3 = new Android(3);
       
        System.out.println(i5.compareTo(i4));
        System.out.println(a3.compareTo(a2));
        // 错误,两种手机之间不能比较系统版本大小
        //System.out.println(i5.compareTo(a3));
    }
}

作者:wenyinfeng

转载时,请注明原文出处, 谢谢!