(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)
上一篇 下一篇

猜你喜欢

热点阅读