枚举类型浅析

回头再写备注

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 条评论
登录 后参与评论

相关文章

来自专栏mathor

线性表(一)

652
来自专栏Java3y

十道算法题[二]

前言 清明不小心就拖了两天没更了~~ 这是十道算法题的第二篇了~上一篇回顾:十道简单算法题 最近在回顾以前使用C写过的数据结构和算法的东西,发现自己的算法和数据...

3049
来自专栏swag code

抽象类与抽象方法

在我们抽象实例对象的时候,有这样一种情况,往上层抽象时就会发现很难描述对象的属性和行为,比如“形状” ,其方法计算面积怎么计算?正方形知道怎么计算,长方形也知道...

683
来自专栏LeetCode

LeetCode 46 & 47. Permutations I&II

Given a collection of distinct integers, return all possible permutations.

980
来自专栏DT乱“码”

Java 枚举类型enum 的使用

Java 枚举类型enum 的使用 最近跟同事讨论问题的时候,突然同事提到我们为什么java 中定义的常量值不采用enmu 枚举类型,而采用public fin...

1799
来自专栏Golang语言社区

Golang语言关于零值的定义

原文:https://golang.org/ref/spec#The_zero_value The 零值 当一个变量或者新值被创建时, 如果没有为其明确指定初始...

34211
来自专栏Android知识点总结

再见kotlin----01语句控制

602
来自专栏有趣的Python

慕课网-C++远征之多态篇(中)-学习笔记

c++远征之多态篇 纯虚函数 & 抽象类 例子: class Shape { public: virtual double calcArea()//虚函...

2984
来自专栏苦逼的码农

从零打卡leetcode之day 2--两数相加

我靠,居然还用到了链表的知识,突然就想起了当初用c语言自学链表的那段日子,真的差点被搞死。各种指针指来指去的。

882
来自专栏测试开发架构之路

程序员面试50题(2)—二元查找树的后序遍历结果[数据结构]

题目:输入一个整数数组,判断该数组是不是某二元查找树的后序遍历的结果。如果是返回true,否则返回false。 例如输入5、7、6、9、11、10、8,由于这一...

2596

扫码关注云+社区