(9)枚举反射的基本使用
2018-10-06 本文已影响0人
Mrsunup
上一小节讲解了数组的基本用法,包括如何用反射创建一个数组以及给数组元素进行设置,以及判断一个对象是否为数组类型,获取数组元素的单元类型。
这一小节,将介绍反射中关于枚举的用法,并不像数组一样,枚举并没有单独的类来操作枚举,打算从下面的几个方面讲解:
检测枚举
设置和访问枚举字段
枚举反射错误
1.检测枚举
直接给出下面的例子:
/**
* 枚举检测类
*Class.getEnumConstants() 获取枚举类型的实例 ,Class.getEnumConstants这个方法的本质是通过反射调用values方法获取枚举的实例
*
*具体枚举类.values() 也是获取枚举类型的实例
*java.lang.reflect.Field.isEnumConstant() 判断字段是否是枚举类型
*/
public class EnumConstants {
public static void main(String... args) {
args = new String[]{RetentionPolicy.class.getName()};
try {
Class<?> c = Class.forName(args[0]);
//Class.isEnum() 表明该类是否是枚举类
System.out.println("isEnum : "+ c.isEnum());
//Class.getEnumConstants() 返回类中声明的枚举实例 等同于 RetentionPolicy.values();
out.format("Enum name: %s%nEnum constants: %s%n",
c.getName(), Arrays.asList(c.getEnumConstants()));
for(Field field : c.getDeclaredFields()){
if(field.isEnumConstant()){
System.out.println(field.toGenericString());
}
}
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
}
输出结果为:
isEnum : true
Enum name: java.lang.annotation.RetentionPolicy
Enum constants: [SOURCE, CLASS, RUNTIME]
public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.SOURCE
public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.CLASS
public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.RUNTIME
在这里延伸一点,为什么class.getEnumConstants()本质上等同于具体枚举类.values(),先看看class.getEnumConstants()
public T[] getEnumConstants() {
T[] values = getEnumConstantsShared();
return (values != null) ? values.clone() : null;
}
T[] getEnumConstantsShared() {
if (enumConstants == null) {
if (!isEnum()) return null;//先判断是否是枚举类
try {
final Method values = getMethod("values"); //得到该枚举类的values方法
java.security.AccessController.doPrivileged(
new java.security.PrivilegedAction<Void>() {
public Void run() {
values.setAccessible(true); //设置访问权限为true
return null;
}
});
@SuppressWarnings("unchecked")
T[] temporaryConstants = (T[])values.invoke(null); //进行反射调用获取
enumConstants = temporaryConstants;
}
// These can happen when users concoct enum-like classes
// that don't comply with the enum spec.
catch (InvocationTargetException | NoSuchMethodException |
IllegalAccessException ex) { return null; }
}
return enumConstants;
}
还有一个例子,关于枚举扫描的例子,从这个例子可以看出编译器会自动生成values()方法和valueof方法以及合成了个$VALUES的字段
/**
* 枚举扫描
*
*/
public class EnumSpy {
private static final String fmt = " %11s: %s %s%n";
public static void main(String... args) {
args = new String[]{"java.lang.annotation.RetentionPolicy"};
try {
Class<?> c = Class.forName(args[0]);
if (!c.isEnum()) {
out.format("%s is not an enum type%n", c);
return;
}
out.format("Class: %s%n", c);
Field[] flds = c.getDeclaredFields();
List<Field> cst = new ArrayList<Field>(); // enum constants
List<Field> mbr = new ArrayList<Field>(); // member fields
for (Field f : flds) {
if (f.isEnumConstant())
cst.add(f);
else
mbr.add(f);
}
if (!cst.isEmpty())
print(cst, "Constant");
if (!mbr.isEmpty())
print(mbr, "Field");
Constructor[] ctors = c.getDeclaredConstructors();
for (Constructor ctor : ctors) {
out.format(fmt, "Constructor", ctor.toGenericString(),
synthetic(ctor));
}
Method[] mths = c.getDeclaredMethods();
for (Method m : mths) {
out.format(fmt, "Method", m.toGenericString(),
synthetic(m));
}
// production code should handle this exception more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
}
}
private static void print(List<Field> lst, String s) {
for (Field f : lst) {
out.format(fmt, s, f.toGenericString(), synthetic(f));
}
}
private static String synthetic(Member m) {
return (m.isSynthetic() ? "[ synthetic ]" : "");
}
}
输出结果为:
Class: class java.lang.annotation.RetentionPolicy
Constant: public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.SOURCE
Constant: public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.CLASS
Constant: public static final java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.RUNTIME
Field: private static final java.lang.annotation.RetentionPolicy[] java.lang.annotation.RetentionPolicy.$VALUES [ synthetic ]
Constructor: private java.lang.annotation.RetentionPolicy()
Method: public static java.lang.annotation.RetentionPolicy[] java.lang.annotation.RetentionPolicy.values()
Method: public static java.lang.annotation.RetentionPolicy java.lang.annotation.RetentionPolicy.valueOf(java.lang.String)
2.设置和访问枚举字段
内容比较简单,直接给出例子:
import java.lang.reflect.Field;
import static java.lang.System.out;
/**
* 枚举的设置 跟字段设置一样,
* Field.get(object)
* Filed.set(object,enum)
*
*
*/
public class SetTrace {
public static void main(String... args) {
args = new String []{"LOW"};
TraceLevel newLevel = TraceLevel.valueOf(args[0]);
try {
MyServer svr = new MyServer();
Class<?> c = svr.getClass();
Field f = c.getDeclaredField("level");
f.setAccessible(true);
TraceLevel oldLevel = (TraceLevel) f.get(svr);
out.format("Original trace level: %s%n", oldLevel);
if (oldLevel != newLevel) {
f.set(svr, newLevel);
out.format(" New trace level: %s%n", f.get(svr));
}
// production code should handle these exceptions more gracefully
} catch (IllegalArgumentException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
} catch (NoSuchFieldException x) {
x.printStackTrace();
}
}
}
enum TraceLevel {OFF, LOW, MEDIUM, HIGH, DEBUG}
class MyServer {
private TraceLevel level = TraceLevel.OFF;
}
输出结果为:
Original trace level: OFF
New trace level: LOW
3.枚举反射错误
不能通过反射创建枚举类型 ,会报错
enum Charge {
POSITIVE, NEGATIVE, NEUTRAL;
Charge() {
out.format("under construction%n");
}
}
/**
* 不能通过反射创建枚举类型 ,会报错
*
*/
public class EnumTrouble {
public static void main(String... args) {
try {
Class<?> c = Charge.class;
Constructor[] ctors = c.getDeclaredConstructors();
for (Constructor ctor : ctors) {
out.format("Constructor: %s%n", ctor.toGenericString());
ctor.setAccessible(true);
ctor.newInstance();
}
// production code should handle these exceptions more gracefully
} catch (InstantiationException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
} catch (InvocationTargetException x) {
x.printStackTrace();
}
}
}
报错信息如下:
Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
at com.java.reflect.arraysAndEnumeratedTypes.enumTypes.EnumTrouble.main(EnumTrouble.java:28)
Constructor: private com.java.reflect.arraysAndEnumeratedTypes.enumTypes.Charge()
设置不相容的枚举值会报错
import java.lang.reflect.Field;
enum E0 { A, B }
enum E1 { A, B }
class ETest {
private E0 fld = E0.A;
}
/**
* 设置不相容的枚举值会报错
*
* 设置的值必须满足下面的情况,X为声明的枚举类型,y为设置的枚举值
* * X.class.isAssignableFrom(Y.class) == true
* 必须为x的子类或者就是x
*
*/
public class EnumTroubleToo {
public static void main(String... args) {
try {
ETest test = new ETest();
Field f = test.getClass().getDeclaredField("fld");
f.setAccessible(true);
f.set(test, E1.A); // IllegalArgumentException
// production code should handle these exceptions more gracefully
} catch (NoSuchFieldException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
}
}
}
报错信息如下:
Exception in thread "main" java.lang.IllegalArgumentException: Can not set com.java.reflect.arraysAndEnumeratedTypes.enumTypes.E0 field com.java.reflect.arraysAndEnumeratedTypes.enumTypes.ETest.fld to com.java.reflect.arraysAndEnumeratedTypes.enumTypes.E1
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:81)
at java.lang.reflect.Field.set(Field.java:764)
at com.java.reflect.arraysAndEnumeratedTypes.enumTypes.EnumTroubleToo.main(EnumTroubleToo.java:27)