Java 7 源码学习系列(二)——Enum

Enum

Enum类是java.lang包中一个类,他是Java语言中所有枚举类型的公共基类。

一、定义

publicabstractclassEnum<EextendsEnum<E>>implementsComparable<E>,Serializable

1.抽象类。

首先, 抽象类不能被实例化 ,所以我们 在java程序中不能使用new关键字来声明一个Enum ,如果想要定义可以使用这样的语法:

enum enumName{

    value1,value2

    method1(){}

    method2(){}

}

其次,看到抽象类,第一印象是肯定有类继承他。至少我们应该是可以继承他的,所以:

/**

* @author hollis

*/publicclasstestEnumextendsEnum{}publicclasstestEnumextendsEnum<Enum<E>>{}publicclasstestEnum<E>extendsEnum<Enum<E>>{}

尝试了以上三种方式之后,得出以下结论: Enum类无法被继承 。

为什么一个抽象类不让继承?enum定义的枚举是怎么来的?难道不是对Enum的一种继承吗?带着这些疑问我们来反编译以下代码:

enumColor{RED, BLUE, GREEN}

编译器将会把他转成如下内容:

/** *@authorhollis */publicfinalclassColorextendsEnum{publicstaticfinalColor[] values() {return(Color[])$VALUES.clone(); }publicstaticColor valueOf(String name) { ... }privateColor(String s, int i) { super(s, i); }publicstaticfinalColor RED;publicstaticfinalColor BLUE;publicstaticfinalColor GREEN;privatestaticfinalColor $VALUES[];static{    RED =newColor("RED",0);    BLUE =newColor("BLUE",1);    GREEN =newColor("GREEN",2);    $VALUES = (newColor[] { RED, BLUE, GREEN });  }}

短短的一行代码,被编译器处理过之后竟然变得这么多,看来,enmu关键字是java提供给我们的一个语法糖啊。。。从反编译之后的代码中,我们发现,编译器不让我们继承Enum,但是当我们使用enum关键字定义一个枚举的时候,他会帮我们在编译后默认继承java.lang.Enum类,而不像其他的类一样默认继承Object类。且采用enum声明后,该类会被编译器加上final声明,故该类是无法继承的。 PS:由于JVM类初始化是线程安全的,所以可以采用枚举类实现一个线程安全的单例模式。

2.实现 Comparable 和 Serializable 接口。

Enum实现了Serializable接口,可以序列化。 Enum实现了Comparable接口,可以进行比较,默认情况下,只有同类型的enum才进行比较(原因见后文),要实现不同类型的enum之间的比较,只能复写compareTo方法。

3.泛型: **<E extends Enum<E>>**

怎么理解 <E extends Enum<E>> ?

首先,这样写只是为了让Java的API更有弹性,他主要是限定形态参数实例化的对象,故要求只能是Enum,这样才能对 compareTo 之类的方法所传入的参数进行形态检查。所以, 我们完全可以不必去关心他为什么这么设计。

进群:697699179可以获取Java各类入门学习资料!

这是我的微信公众号【编程study】各位大佬有空可以关注下,每天更新Java学习方法,感谢!

学习中遇到问题有不明白的地方,推荐加小编Java学习群:697699179内有视频教程 ,直播课程 ,等学习资料,期待你的加入

这里倒是可以关注一下泛型中extends的用法,以及 K V O T E ? object 这几个符号之间的区别 

好啦,我们回到这个令人实在是无法理解的 <E extends Enum<E>>

首先我们先来“翻译”一下这个 Enum<E extends Enum<E>> 到底什么意思, 然后再来解释为什么Java要这么用。 我们先看一个比较常见的泛型: List<String> 。这个泛型的意思是,List中存的都是String类型,告诉编译器要接受String类型,并且从List中取出内容的时候也自动帮我们转成String类型。 所以 Enum<E extends Enum<E>> 可以暂时理解为Enum里面的内容都是 E extends Enum<E> 类型。 这里的 E 我们就理解为枚举,extends表示上界,比如: List<? extends Object> ,List中的内容可以是Object或者扩展自Object的类。这就是extends的含义。 所以, E extends Enum<E> 表示为一个继承了 Enum<E> 类型的枚举类型。 那么, Enum<E extends Enum<E>> 就不难理解了,就是一个Enum只接受一个Enum或者他的子类作为参数。相当于把一个子类或者自己当成参数,传入到自身,引起一些特别的语法效果。

为什么Java要这样定义Enum

首先我们来科普一下enum,

/**

* @author hollis

*/enumColor{RED,GREEN,YELLOW}enumSeason{SPRING,SUMMER,WINTER}publicclassEnumTest{publicstaticvoid main(String[] args) {System.out.println(Color.RED.ordinal());System.out.println(Season.SPRING.ordinal());    }}

代码中两处输出内容都是 0 ,因为枚举类型的默认的序号都是从零开始的。

要理解这个问题,首先我们来看一个Enum类中的方法(暂时忽略其他成员变量和方法):

/**

* @author hollis

*/publicabstractclassEnum<EextendsEnum<E>>implementsComparable<E>,Serializable{privatefinalint ordinal;        publicfinalint compareTo(Eo) {Enumother = (Enum)o;Enumself =this;if(self.getClass() != other.getClass() &&// optimizationself.getDeclaringClass() != other.getDeclaringClass())thrownewClassCastException();returnself.ordinal - other.ordinal;    }}

首先我们认为Enum的定义中没有使用 Enum<E extends Enum<E>> ,那么compareTo方法就要这样定义(因为没有使用泛型,所以就要使用Object,这也是Java中很多方法常用的方式):

publicfinalintcompareTo(Object o)

当我们调用compareTo方法的时候依然传入两个枚举类型,在compareTo方法的实现中,比较两个枚举的过程是先将参数转化成Enum类型,然后再比较他们的序号是否相等。那么我们这样比较:

Color.RED.compareTo(Color.RED);Color.RED.compareTo(Season.SPRING);

如果在 compareTo 方法中不做任何处理的话,那么以上这段代码返回内容将都是true(因为Season.SPRING的序号和Color.RED的序号都是 0 )。但是,很明显, Color.RED 和 Season.SPRING 并不相等。

但是Java使用 Enum<E extends Enum<E>> 声明Enum,并且在 compareTo 的中使用 E 作为参数来避免了这种问题。 以上两个条件限制Color.RED只能和Color定义出来的枚举进行比较,当我们试图使用 Color.RED.compareTo(Season.SPRING); 这样的代码是,会报出这样的错误:

The method compareTo(Color)inthetypeEnumisnotapplicableforthe arguments (Season)

他说明,compareTo方法只接受 Enum<Color> 类型。

Java为了限定形态参数实例化的对象,故要求只能是Enum,这样才能对 compareTo之类的方法所传入的参数进行形态检查。 因为“红色”只有和“绿色”比较才有意义,用“红色”和“春天”比较毫无意义,所以,Java用这种方式一劳永逸的保证像compareTo这样的方法可以正常的使用而不用考虑类型。

PS:在Java中,其实也可以实现“红色”和“春天”比较,因为Enum实现了 Comparable 接口,可以重写compareTo方法来实现不同的enum之间的比较。

二、成员变量

在Enum中,有两个成员变量,一个是名字(name),一个是序号(ordinal)。 序号是一个枚举常量,表示在枚举中的位置,从0开始,依次递增。

/** *@authorhollis */privatefinalString name;publicfinalStringname(){returnname;}privatefinalintordinal;publicfinalintordinal(){returnordinal;}

三、构造函数

前面我们说过,Enum是一个抽象类,不能被实例化,但是他也有构造函数,从前面我们反编译出来的代码中,我们也发现了Enum的构造函数,在Enum中只有一个保护类型的构造函数:

protectedEnum(String name, int ordinal) {this.name = name;this.ordinal = ordinal;}

文章开头反编译的代码中 private Color(String s, int i) { super(s, i); } 中的 super(s, i); 就是调用Enum中的这个保护类型的构造函数来初始化name和ordinal。

四、其他方法

Enum当中有以下这么几个常用方法,调用方式就是使用 Color.RED.methodName(params...) 的方式调用

publicString toString() {returnname;}publicfinalbooleanequals(Object other) {returnthis==other;}publicfinalinthashCode() {returnsuper.hashCode();}publicfinalintcompareTo(E o) {    Enum other = (Enum)o;    Enum self =this;if(self.getClass() != other.getClass() &&// optimizationself.getDeclaringClass() != other.getDeclaringClass())thrownewClassCastException();returnself.ordinal - other.ordinal;}publicfinalClass getDeclaringClass() {Classclazz = getClass();Classzuper = clazz.getSuperclass();return(zuper == Enum.class) ? clazz : zuper;}publicstatic> T valueOf(Class enumType,String name) {    T result = enumType.enumConstantDirectory().get(name);if(result !=null)returnresult;if(name ==null)thrownewNullPointerException("Name is null");thrownewIllegalArgumentException("No enum constant "+ enumType.getCanonicalName() +"."+ name);}

方法内容都比较简单,平时能使用的就会也不是很多,这里就不详细介绍了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,921评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,635评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,393评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,836评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,833评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,685评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,043评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,694评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,671评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,670评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,779评论 1 332
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,424评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,027评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,984评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,214评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,108评论 2 351
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,517评论 2 343

推荐阅读更多精彩内容

  • 简介 枚举是Java1.5引入的新特性,通过关键字enum来定义枚举类。枚举类是一种特殊类,它和普通类一样可以使用...
    JimmieYang阅读 42,559评论 4 75
  • 有的人说,不推荐使用枚举。有的人说,枚举很好用。究竟怎么使用,如何使用,仁者见仁智者见智。总之,先学会再说~ 为什...
    秋刀鱼茶泡饭QAQ阅读 869评论 0 5
  • 对象的创建与销毁 Item 1: 使用static工厂方法,而不是构造函数创建对象:仅仅是创建对象的方法,并非Fa...
    孙小磊阅读 1,963评论 0 3
  • 1. 概述 枚举(enum)全写为的全称为:enumeration。是jdk1.5才新引进的概念,在Java中en...
    WY长河阅读 690评论 0 0
  • 本文包括:枚举由来如何使用?枚举类特性单例设计模式定义特殊结构枚举星期输出中文的案例枚举类API 枚举(enum)...
    廖少少阅读 2,838评论 2 14