JAVA枚举基础

枚举类型(enum type)是指由一组固定的常量组成合法值的类型,例如一年中的季节、太阳系中的行星或者一副牌中的花色。

An enumerated type is a type whose legal values consist of a fixed set of constants,such as the seasons of the year, the planets in the solar system, or the suits in a deck of playing cards. )


Java的枚举本质上是int值,它的基本想法是: 通过公有的静态的final域为每个枚举常量导出实例的类。

they are classes that export one instance for each enumeration constant via a public static final field.

 

也就是说它的每个枚举的实例类修饰符为public static final

由于枚举都继承自java.lang.Enum类,而java不支持多继承,但是可以实现一个或者多个接口。对于enum而言,实现接口是其子类化的唯一方法可以看个例子:

public interface Food {
  enum Appetizer implements Food {
    SALAD, SOUP, SPRING_ROLLS;
  }
  enum MainCourse implements Food {
    LASAGNE, BURRITO, PAD_THAI,
    LENTILS, HUMMOUS, VINDALOO;
  }
  enum Dessert implements Food {
    TIRAMISU, GELATO, BLACK_FOREST_CAKE,
    FRUIT, CREME_CARAMEL;
  }
  enum Coffee implements Food {
    BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,
    LATTE, CAPPUCCINO, TEA, HERB_TEA;
  }
}

 下面的程序说明所以东西都是某种类型的Food:

import static Food.*;

public class TypeOfFood {
  public static void main(String[] args) {
    Food food = Appetizer.SALAD;
    food = MainCourse.LASAGNE;
    food = Dessert.GELATO;
    food = Coffee.CAPPUCCINO;
  }
}
 

事实上看反编译的字节码也能看出一些东西,我使用的方便易工具是JDK(1.6)自带的javap工具,关于它的一些基本使用,可以参考文档:http://docs.oracle.com/javase/6/docs/technotes/tools/windows/javap.html

Planet类(下面的具体实例)字节码反编译的部分代码:

public final class test.effective.chapter6.Planet extends java.lang.Enum{
public static final test.effective.chapter6.Planet MERCURY;

public static final test.effective.chapter6.Planet VENUS;

public static final test.effective.chapter6.Planet EARTH;

public static final test.effective.chapter6.Planet MARS;

public static final test.effective.chapter6.Planet JUPITER;

public static final test.effective.chapter6.Planet SATURN;

public static final test.effective.chapter6.Planet URANUS;

public static final test.effective.chapter6.Planet NEPTUNE;
 

一个具体的实例:

 

// Enum type with data and behavior
public enum Planet {
	MERCURY(3.302e+23, 2.439e6), VENUS(4.869e+24, 6.052e6), EARTH(5.975e+24,
			6.378e6), MARS(6.419e+23, 3.393e6), JUPITER(1.899e+27, 7.149e7), SATURN(
			5.685e+26, 6.027e7), URANUS(8.683e+25, 2.556e7), NEPTUNE(1.024e+26,
			2.477e7);
	private final double mass; // In kilograms
	private final double radius; // In meters
	private final double surfaceGravity; // In m / s^2

	// Universal gravitational constant in m^3 / kg s^2
	private static final double G = 6.67300E-11;

	// Constructor,构造器只能是私有的private
	// 这样可以保证外部代码无法新构造枚举类的实例。
	// 这也是完全符合情理的,因为我们知道枚举值是public static final的常量而已。
	private Planet(double mass, double radius) {
		this.mass = mass;
		this.radius = radius;
		surfaceGravity = G * mass / (radius * radius);
	}

	public double mass() {
		return mass;
	}

	public double radius() {
		return radius;
	}

	public double surfaceGravity() {
		return surfaceGravity;
	}

	public double surfaceWeight(double mass) {
		return mass * surfaceGravity; // F = ma
	}

	// 根据某一物体在地球上的重量(175),显示出该物体在所有八颗行星上的重量。
	public static void main(String[] args) {
		double earthWeight = Double.parseDouble("175");
		double mass = earthWeight / Planet.EARTH.surfaceGravity();
		// Planet.values(),按照枚举的声明顺序返回它的值数组(returns an array of its values in
		// the order they were declared)
		for (Planet p : values())
			// s表示字符串,f表示浮点数,n表示与平台无关的换行符
			System.out.printf("Weight on %s is %f%n", p, p.surfaceWeight(mass));
	}
}

 刚开始看这个例子的时候,不知道values()方法是如何来的,后来看了一下(JLS8.9 ),感觉有点明白了,它里面有这么一段解释:

In addition, if E is the name of an enum type, then that type has the following implicitly declared static methods:


    /**

    * Returns an array containing the constants of this enum 
    * type, in the order they're declared.  This method may be
    * used to iterate over the constants as follows:
    *
    *    for(E c : E.values())
    *        System.out.println(c);
    *
    * @return an array containing the constants of this enum 
    * type, in the order they're declared
    */
    public static E[] values();

    /**
    * Returns the enum constant of this type with the specified
    * name.
    * The string must match exactly an identifier used to declare
    * an enum constant in this type.  (Extraneous whitespace 
    * characters are not permitted.)
    * 
    * @return the enum constant with the specified name
    * @throws IllegalArgumentException if this enum type has no
    * constant with the specified name
    */
    public static E valueOf(String name);

 

也就是说,这两个静态方法[ values() & valueOf(String name) ]是“隐藏”的,事实上这两个方法是由编译器添加的static方法。

我用JDK自带的javap工具生成的部分代码是这样的(cd到字节码所在的目录,使用命令javap -c Planet ):

public static test.effective.chapter6.Planet[] values();
  Code:
   0:   getstatic       #87; //Field ENUM$VALUES:[Ltest/effective/chapter6/Plane
t;
   3:   dup
   4:   astore_0
   5:   iconst_0
   6:   aload_0
   7:   arraylength
   8:   dup
   9:   istore_1
   10:  anewarray       #1; //class test/effective/chapter6/Planet
   13:  dup
   14:  astore_2
   15:  iconst_0
   16:  iload_1
   17:  invokestatic    #149; //Method java/lang/System.arraycopy:(Ljava/lang/Ob
ject;ILjava/lang/Object;II)V
   20:  aload_2
   21:  areturn

 

关于这些指令的大致意思,可以参考下面的这篇文章:Java二进制指令代码解析 。当然,最好的方法是查看java虚拟机规范,可以去这个网址 下载java se7的规范中文版。在最后,作者加入了这些指令的含义。

 

实例二:

如果多个枚举常量同时共享相同的行为,可以考虑策略枚举(Strategy enum)【from efective java 2】。

// The strategy enum pattern
public enum PayrollDay {
	MONDAY(PayType.WEEKDAY), TUESDAY(PayType.WEEKDAY), WEDNESDAY(
			PayType.WEEKDAY), THURSDAY(PayType.WEEKDAY), FRIDAY(PayType.WEEKDAY), SATURDAY(
			PayType.WEEKEND), SUNDAY(PayType.WEEKEND);

	private final PayType payType;

	PayrollDay(PayType payType) {
		this.payType = payType;
	}

	double pay(double hoursWorked, double payRate) {
		return payType.pay(hoursWorked, payRate);
	}

	// The strategy enum type
	private enum PayType {
		WEEKDAY {
			double overtimePay(double hours, double payRate) {
				return hours <= HOURS_PER_SHIFT ? 0 : (hours - HOURS_PER_SHIFT)
						* payRate / 2;
			}
		},
		WEEKEND {
			double overtimePay(double hours, double payRate) {
				return hours * payRate / 2;
			}
		};
		private static final int HOURS_PER_SHIFT = 8;

		abstract double overtimePay(double hrs, double payRate);

		double pay(double hoursWorked, double payRate) {
			double basePay = hoursWorked * payRate;
			return basePay + overtimePay(hoursWorked, payRate);
		}
	}

	public static void main(String[] args) {
		PayrollDay payroll = PayrollDay.MONDAY;
		System.out.println(payroll.pay(8, 20));
	}
}
 



参考资料:

effective java second edition item30

JLS8.9

core java 卷一

Thinking in java(4th)   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值