枚举类型浅析

回头再写备注

1、枚举类就是class并且是一个不可被继承的final class,其枚举值都是public static final的。

2、既然枚举类是class其就会有构造、方法、数据域。但是枚举类的构造器有很大不同:首先,构造器只是在构造枚举值时使用;其次,构造器是private的,不允许为public(这样可以保证外部代码无法新构造枚举类的实例。这也是完全符合情理的,因为我们知道枚举值是public static final的常量而已。 但枚举类的方法和数据域可以允许外部访问)。

3、所有枚举类都继承了Enum的方法,下面我们详细介绍这些方法。   (1)  ordinal()方法: 返回枚举值在枚举类种的顺序。这个顺序根据枚举值声明的顺序而定。  (2)  compareTo()方法: Enum实现了java.lang.Comparable接口,因此可以比较象与指定对象的顺序。Enum中的compareTo返回的是两个枚举值的顺序之差。当然,前提是两个枚举值必须属于同一个枚举类,否则会抛出ClassCastException()异常。  (3)  values()方法: 静态方法,返回一个包含全部枚举值的数组。  (4)  toString()方法: 返回枚举常量的名称。  (5)  valueOf()方法: 这个方法和toString方法是相对应的,返回带指定名称的指定枚举类型的枚举常量。  (6)  equals()方法: 比较两个枚举类对象的引用。

4、枚举类可以在switch语句中使用。

5.特定于常量的枚举方法

6.策略枚举(strategy enum)

package cn.com.imooc;

//Enum type with data and behavior
public enum Planet {

	MERCURY(3.302e+23,2.439e6),
	VENUS(4.869E+24,6.052e6),
	EARCH(5.975e+24,6.378e6),
	MARS (6.419e+23,3.393e6),
	JUPITER(1.899e+27,7.149e7),
	STATURN(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
	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
	}
}

测试:

package cn.com.test;

import cn.com.imooc.Planet;

public class WeightTable {

	public static void main(String[] args) {
		
		double earthWeight = Double.parseDouble("175");
		double mass = earthWeight/Planet.EARCH.surfaceGravity();
		for(Planet p : Planet.values()){
			System.out.printf("Weight on %s is %f%n",p,p.surfaceWeight(mass));
		}
	}
}

枚举类实现最基本的加减乘除:

package cn.com.imooc;


//Enum type that switches on its own value -questionable
/**
 * 枚举类实现最基本的加减乘除
 * @author xiaomingtongxie
 *
 */
public enum Operation {
	PLUS,MINUS,TIMES,DIVIDE;
	
	//Do the arithmetic op represented by this constant
	double apply(double x, double y){
		switch(this){
			case PLUS: return x + y;
			case MINUS: return x - y;
			case TIMES: return x * y;
			case DIVIDE: return x / y;
		}
		throw new AssertionError("Unkonwn op:" + this);
	}

}

枚举类型是编译安全类型,加入增加了新的运算枚举类,而没有在switch 中添加对应的条件,编译时不会报错,但运行时会出错。

于是有了,特定于常量的方法实现,优化代码如下:

package cn.com.imooc;

//Enum type with constant-specific method implementations
public enum Operation {
	PLUS {
		double apply(double x, double y) {
			return x + y;
		}
	},
	MINUS {
		double apply(double x, double y) {
			return x - y;
		}
	},
	TIMES {
		double apply(double x, double y) {
			return x * y;
		}
	},
	DIVIDE {
		double apply(double x, double y) {
			return x / y;
		}
	};

	abstract double apply(double x, double y);
}

枚举类中的抽象方法必须被它所有常量中的具体方法所覆盖,这样就不存在有编译时错误了。

package cn.com.imooc;

//Enum type with constant-specific method implementations
public enum Operation {
	//枚举类中的抽象方法必须被它所有常量中的具体方法所覆盖
	PLUS("+") {
		double apply(double x, double y) {
			return x + y;
		}
	},
	MINUS("-") {
		double apply(double x, double y) {
			return x - y;
		}
	},
	TIMES("*") {
		double apply(double x, double y) {
			return x * y;
		}
	},
	DIVIDE("/") {
		double apply(double x, double y) {
			return x / y;
		}
	};
	
	private final String symbol;
	Operation(String symbol){this.symbol = symbol;}
	@Override public String toString(){return symbol;}
	
	abstract double apply(double x, double y);
}

测试:

public static void main(String[] args) {
		//Operation obj = Operation.MINUS;
		double x = Double.parseDouble("2");
		double y = Double.parseDouble("4");
		for (Operation obj : Operation.values()) {
			System.out.printf("%f %s %f = %f%n",x,obj,y,obj.apply(x, y));
		}
	}

在枚举类中覆盖toString,要考虑编写一个fromString方法,见定制的字符串表示法变回相应的枚举。

//Implementing a fromString method on an enum type
	private static final Map<String,Operation> stringToEnum = new HashMap<String, Operation>();
	static{ // Initialize map from constant name to enum constant
		for (Operation obj : values()) {
			stringToEnum.put(obj.toString(), obj);
		}
	}
	
	//Return Operation for string, or null if string is invalid
	public static Operation fromString(String symbol)
	{
		return stringToEnum.get(symbol);
	}

试图使每个常量都从自己的构造器将自身放入到map中,会导致编译时错误。枚举构造器不可以访问枚举的静态域,除了编译时常量域除外。

//实现工作日正常工资,双休加班工资是平时一半

package cn.com.imooc;

//Enum tha switches on its value to share code-questionable
public enum PayrollDay {

	MONDAY, TUESDAY, WEDNESDAY, THURSDAY, FRIDAY, SATURDAY, SUNDAY;
	private static final int HOURS_PER_SHIFT = 8;

	double pay(double hourWorked, double payRate) {
		double basePay = hourWorked * payRate;

		double overtimePay; // Calculate overtime pay
		switch (this) {
		case SATURDAY:
		case SUNDAY:
			overtimePay = hourWorked * payRate / 2;
		default: // Weekdays
			overtimePay = hourWorked <= HOURS_PER_SHIFT ? 0 : (hourWorked
					* payRate / 2);
			break;
		}
		return basePay + overtimePay;
	}
}

但是,如果添加了个特殊的枚举,代表假期天数的特殊值,但忘记添加相应的case.程序依然可以编译,但pay方法会悄悄地将假期的工资算成域常量工作日的相同。

每当添加一个枚举常量时,就强制选择一种加班报酬策略。

下面就是策略枚举的实现:

package cn.com.imooc;

//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);
		}
	}
}

这种思想就是将加班工资计算一道一个私有的嵌套枚举中,将这个策略枚举的实例转到PayrollDay枚举的构造器中。

当然,switch 还是有用的,比如说加减法倒置,乘除法倒置

//Switch on an enum to simulate a missing method
	public static Operation inverse(Operation op){
		switch (op) {
		case PLUS: return Operation.MINUS;
		case MINUS:return Operation.PLUS;
		case TIMES:return Operation.DIVIDE;
		case DIVIDE:return Operation.TIMES;
		default: throw new AssertionError("Unkown op:" + op);
		}
	}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏机器学习入门

挑战程序竞赛系列(86):3.6极限情况(3)

挑战程序竞赛系列(86):3.6极限情况(3) 传送门:AOJ 2201: Immortal Jewels 翻译参考至hankcs: http://www....

22310
来自专栏Spark学习技巧

Java枚举(enum)七种常见的用法

DK1.5引入了新的类型——枚举。在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便。 用法一:常量 在JDK1.5 之前,我们定义常量都是:...

2753
来自专栏java达人

Java 枚举7常见种用法

用法一:常量 在JDK1.5 之前,我们定义常量都是: public static fianl…. 。现在好了,有了枚举,可以把相关的常量分组到一个枚举类型里,...

2568
来自专栏冰霜之地

ReactiveCocoa 中 集合类 RACSequence 和 RACTuple 底层实现分析

在OOP的世界里使用FRP的思想来编程,光有函数这种一等公民,还是无法满足我们一些需求的。因此还是需要引用变量来完成各式各样的类的操作行为。

1335
来自专栏wannshan(javaer,RPC)

dubbo序列化过程源码分析

先看下dubbo在serialize层的类设计方案 序列化方案的入口,是接口Serialization的实现类。 /** * Serialization. ...

8829
来自专栏微信公众号:Java团长

Java枚举(enum)七种常见的用法

DK1.5引入了新的类型——枚举。在 Java 中它虽然算个“小”功能,却给我的开发带来了“大”方便。

1154
来自专栏前端布道

JavaScript之对象继承

该方法创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象。

973
来自专栏進无尽的文章

编码篇 - 正则表达式及其相关

有时我们需要在一大段长文本中过滤出我们需要的字段,或者检验该文本是否符合要求(该文本是否是邮箱,链接,电话号码或身份证),这时候就需要用到正则表达式了,当然我们...

1192
来自专栏静默虚空的博客

Java 枚举用法详解

概念 enum 的全称为 enumeration, 是 JDK 1.5 中引入的新特性。 在Java中,被 enum 关键字修饰的类型就是枚举类型。形式如下: ...

37510
来自专栏数据之美

Python Tips, Tricks, and Hacks

一、快速技巧 1.1、4 种引号 '  '''  "  """  print """I wish that I'd never heard him say...

2735

扫码关注云+社区

领取腾讯云代金券