您的位置:首页 > 移动开发 > Android开发

Android 反射枚举Enum类型应用

2015-12-08 19:59 459 查看
网上关于反射枚举的案例似乎不多,也许是因为枚举在java里面枚举类型其实算个准类了,java编译的时候同样会生成一个enumname.Class文件,同时Enum是可以被子类直接继承的,所以有时候在反编译的时候反过跟头,掉过阴沟,真的坐船翻船,坐车爆胎,走路都要被石头绊倒的郁闷.同样也是证明不懂java基本知识,后果很严重!!!

今天还是靠公司同事提醒了一下,发现枚举类型具有类的一些特性,虽然后面的操作中证实,在反编译后,即使不知道是枚举也没关系,就当做内部类反射处理就好了,不过同事还是给了一个思路,还是非常感谢的.

当时反射接口Interface的时候,没有特别注意一下内部类.

下面自己亲自做了一个测试程序以供参考:

<1> : 新建一个Android工程,工程树如下:

<2>: Android工程里面添加两个按钮,布局如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">

<Button
android:id="@+id/flextestbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/oneplus_flextest" />

<Button
android:id="@+id/flexbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/oneplus_flexenum" />

</LinearLayout>

<3> : 布局中用到的字符串在oneplus_string.xml,这个是新建的一个values文件:

<?xml version="1.0" encoding="utf-8"?>
<resources>

<string name="oneplus_flexenum">Flex Enum Using</string>
<string name="oneplus_flextest">Flext Enum Test</string>

</resources>


<4>: 主体程序如下:
package com.oneplus.enumtype;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import android.os.Bundle;
import android.app.Activity;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;

/**
* @author zhibao.liu
* @date 2015-12-3
* @company : oneplus.Inc
*/

public class OneplusEnumActivity extends Activity implements OnClickListener {

private final static String TAG="oneplus";
private Button mFlexEnumButton;
private Button mFlexEnumTestButton;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.oneplus_enum);

mFlexEnumButton = (Button) findViewById(R.id.flexbutton);
mFlexEnumButton.setOnClickListener(this);

mFlexEnumTestButton = (Button) findViewById(R.id.flextestbutton);
mFlexEnumTestButton.setOnClickListener(this);

}

private void FlexEnum() {

Log.i(TAG,"FlexEnum test .. ...");

try {
//注意forName参数的美元符号
Class clazz = Class
.forName("com.oneplus.enumtype.OneplusEnumActivity$EducationLevelEnum");
Method[] methods = clazz.getDeclaredMethods();

for (int i = 0; i < methods.length; i++) {

Log.i(TAG,"method name : " + methods[i].getName());

}

Field[] fields = clazz.getDeclaredFields();

for (int i = 0; i < methods.length; i++) {

Log.i(TAG,"field name : " + fields[i].getName());

}

} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

private void FlexInsertEnum() {

try {

Class clazz = Class .forName("com.oneplus.enumtype.OneplusEnumActivity");
Class claze = Class .forName("com.oneplus.enumtype.OneplusEnumActivity$EducationLevelEnum");
Class[] clzs = clazz.getDeclaredClasses();

for (int i = 0; i < clzs.length; i++) {

Log.i(TAG,"Class type : " + clzs.getClass().toString());

}

try {
//clzs[0] : 这里是因为这个主类里面只有一个"内部类"/枚举类
//如果主类里面有很多个内部,那也没有关,可以进一步判断内部类里面的Method和Field做进一步识别判断
Method method = clazz.getDeclaredMethod("insertNum", new Class[] { clzs[0] /* claze.getClass() *//* * EducationLevelEnum * .class */});
method.setAccessible(true);
try {
method.invoke(clazz.newInstance(), new Object[] { null });
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

} catch (NoSuchMethodException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

}

public void insertNum(EducationLevelEnum e) { Log.i(TAG,"hi, I am zhibao.liu from oneplus,Inc, you flex enum sucessfully !"); }
public enum EducationLevelEnum {

DEFAULT("0", "------", 0),

HIGHSCHOOLDIPLOMA("1", "High School Diploma", 100),

ASSOCIATEDEGREE("5", "Associate Degree", 150),

BACHELORSDEGREE("2", "Bachelors Degree", 200),

MASTERSDEGREE("3", "Masters Degree", 300),

DOCTORATE("4", "Doctorate", 400);

EducationLevelEnum(String code, String codeDesc, Integer priority) {

this.code = code;

this.codeDesc = codeDesc;

this.priority = priority;

}

private String code;

private String codeDesc;

EducationLevelEnum(String code, String codeDesc) {

this.code = code;

this.codeDesc = codeDesc;

}

private Integer priority;

public Integer getPriority() {

return priority;

}

public String getCode() {

return code;

}

public String getCodeDesc() {

return codeDesc;

}

}

@Override
public void onClick(View v) {
// TODO Auto-generated method stub
int resid = v.getId();
switch (resid) {
case R.id.flexbutton:
FlexInsertEnum();
break;
case R.id.flextestbutton:
FlexEnum();
break;
default:
break;

}
}

}

程序分析如下:

这里没有把枚举放在单独的类中了,做了直接反射主类来获取枚举类型:

首先看FlexEnum()方法中:
Class clazz = Class
.forName("com.oneplus.enumtype.OneplusEnumActivity$EducationLevelEnum");

由于开篇说了枚举类型是个准类,所以在获取包名”类名”时,和获取内部类的方式一样,在主类和内部类/内部枚举之间以美元符号”$”间隔就可以了.

 

然后看FlexInsertEnum()方法中:

为什么还写了这个方法,是因为发现如果内部类或者枚举类型当做参数在方法中传递,如何获取其类型:如下

Private void method(Enum<?>);

而通过反射方法时,第二个参数需要提供方法参数的类型,如下

Clazz.getDeclaredMethod(“method”,Class[]{*});

*代表传递参数的类型,如果是整形,即int.class,那么如果是枚举类型呢,或者内部类呢?这种情况是真实遇见过的,所以才提别提出来,程序里面写了测试方法:

public void insertNum(EducationLevelEnum e) {

Log.i(TAG,"hi, I am zhibao.liu from oneplus,Inc, you flex enum sucessfully !");

}

这个方法的参数需要传递一个枚举类型,那么getDeclaredMethod第二参数如何传递呢,在这个单独demo中,可能可以用EducationLevelEnum.class,那是因为EducationLevelEnum这个枚举类型在自己主类中,但是反射一般都不会反射自己工程中的类,一般都是反射别人的jar中的类,所以上面传递的方式根本不实际,网上很多demo都是这样操作的,让人感觉莫名其妙!
这里特别做了通用处理,其中还包括:

Class clazz = Class
.forName("com.oneplus.enumtype.OneplusEnumActivity");

这里是通过包名形式获取,而不是通过主类名.class的形式处理.这种情况可以使用jar在android系统路径下,这个路径下不是指目录路径下,而是特指引用路径下,即可以直接被用户引用的路径下.如果jar是随意放在Android系统任意路径下,就需要ClassLoader这个神器来处理了.

程序里面获取枚举类型/内部类的类型如下:

Class clazz = Class
.forName("com.oneplus.enumtype.OneplusEnumActivity");
Class[] clzs = clazz.getDeclaredClasses();

先获取主类的clazz对象,然后通过这个clazz获取主类中包括的枚举类型/内部类,这个获取真的很神奇.参考:

[http://download.oracle.com/technetwork/java/javase/6/docs/zh/api/java/lang/Class.html#getDeclaredClasses%28%29],
居然和getDeclaredFields()异曲同工,
getDeclaredFields()获取类中所有声明的基本参数变量,而getDeclaredClasses()获取类中声明的枚举类型/内部类的参数变量.也正是这个方式一击即中获取枚举类型/内部类类型,即可以传入需要的方法中:

Method method = clazz.getDeclaredMethod("insertNum",
new Class[] { clzs[0] /* claze.getClass() *//*
* EducationLevelEnum
* .class
*/});

这里面其实也尝试过:

Class claze = Class
.forName("com.oneplus.enumtype.OneplusEnumActivity$EducationLevelEnum");

即将claze.getClass()作为第二个参数传入getDeclaredMethod中,始终不行,一直卡住!

解决了上面的难题,运行结果,点击第一个按钮:

打印上面信息,说明测试反射枚举类型成功!

再点击第二个按钮:

打印出hi, I am zhibao.liu from oneplus,Inc, you flex enum successfully !信息,说明程序执行insertNum insertNum方法成功.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: