Java枚举和C枚举的转换——JNI笔记
前言
最近在做中间件相关,遇到一个JAVA和C之间枚举值通过JNI传递的问题。我们知道C语言的枚举型是一个集合,其每个枚举成员是一个整型常量;而JAVA的枚举其本质是一个类,继承自Enum类,每个枚举成员是枚举类型常量。他们之间是不同的两种数据类型,不能直接进行转换。Java枚举和C枚举的转换本质是Java枚举类型和int整型的转换。
两种枚举的特点
新创建的枚举类型继承自Enum类。
Enum类有两个成员变量,name和ordinal。name为枚举常量的名字,ordinal为枚举常量声明时的顺序(从0开始)。
Enum类有一个ordinal()方法,用来获取变量ordinal的值;还有一个values()方法用来获取所有的枚举常量,返回值是一个枚举的数组。
JAVA的枚举类型构造函数一定是私有的,因为要保证枚举是单例的。因此我们不能在JNI里使用AllocObject、NewObject这种需要用到构造函数的方法构建一个枚举型。
JAVA枚举类型的成员都是static的,JNI中可以直接访问
C的枚举型中第一个枚举成员的默认值为整型的0,后续枚举成员的值在前一个成员上加1。
一个例子
知道了两种枚举的特点,我们就可以开始进行转换了。下面我们通过一个例子说明如何进行转换:
我的老板有个三岁的女儿,她很喜欢吃水果,她知道苹果是红色的,香蕉是黄色的。可是别的水果呢?于是她希望有个万能的小精灵告诉她水果的颜色。于是老板找到了我……
啥也不说了,开始吧。先定义两个C枚举,用来描述水果和颜色,它们是底层沟通信息用的。
typedef enum {FRUIT_APPLE,FRUIT_GRAPE,FRUIT_BANANA,FRUIT_MAX
}FRUIT;typedef enum {COLOR_RED,COLOR_PURPLE,COLOR_YELLOW,COLOR_MAX
}COLOR;
然后定义两个JAVA枚举,也用来表示水果和颜色,应用间聊天的时候会用到它们进行描述。
public enum EnFruit {APPLE,GRAPE,BANANA,FRUIT_MAX;
}public enum EnColor {RED,PURPLE,YELLOW,COLOR_MAX;
}
很好,我们离成功又进了一步。
现在我们老板的女儿问出了第一个问题:葡萄什么颜色的?
水果颜色识别器:什么?什么水果??什么是葡萄??
应用:就是这个!!这个就是!!!
两位大佬语言不通,鸡同鸭讲,吵成一团。
于是JNI大哥来帮忙。
extern "C" jobject JNICALL Java_com_example_jnitest_MainActivity_getFruitColor(JNIEnv *env, jobject object, jobject objFruit){jclass clazzFruit = env->FindClass("com/example/jnitest/EnFruit"); jmethodID j_method_fruitOrdinal = env->GetMethodID(clazzFruit,"ordinal","()I");int fruit = env->CallIntMethod(objFruit,j_method_fruitOrdinal);COLOR color = get_fruit_color((FRUIT)fruit);//自定义水果颜色识别器,识别水果颜色}
- 他首先通过FindClass查找Java中代表水果的类,这个方法的参数是类的全名。使用UTF-8编码,其中分隔符使用
/
表示。 - 然后通过GetMethodID方法获取水果类的一个实例方法ID。该方法的第一个参数是一个Java类;第二个参数是UTF-8编码的方法名;第三参数是UTF-8编码的方法签名。
- 这里需要注意,GetMethodID获取的方法是非静态的,需要类的实例才能调用它。接下来的CallIntMethod方法就是通过一个类的实例调用了ordinal方法。Call<type>Method这一系列方法的返回值是type;第一个参数就是类的实例(这里类的实例是由Java直接传给JNI的objFruit);第二个参数就是上一步中获取的实例方法ID;后面是可变参数列表。这里的ordinal方法没有参数,故不填写。
现在终于把应用说的水果翻译成水果颜色识别器能看懂的话,识别器很高兴。
水果颜色识别器:这个水果是紫色啦!
应用:紫色?眼前的黑不是黑,你说的紫是什么紫?
语言不通真是烦恼,于是JNI大哥又忙活起来。当然最后不要忘记DeleteLocalRef释放资源,把翻译用的草稿本还回去留给别人继续用。
extern "C" jobject JNICALL Java_com_example_jnitest_MainActivity_getFruitColor(JNIEnv *env, jobject object, jobject objFruit){jclass clazzFruit = env->FindClass("com/example/jnitest/EnFruit");jmethodID j_method_fruitOrdinal = env->GetMethodID(clazzFruit,"ordinal","()I");int fruit = env->CallIntMethod(objFruit,j_method_fruitOrdinal);COLOR color = get_fruit_color((FRUIT)fruit);//自定义水果颜色识别器,识别水果颜色jclass clazzColor = env->FindClass("com/example/jnitest/EnColor");jobject objColor;jfieldID j_fieldID_RED = env->GetStaticFieldID(clazzColor,"RED","Lcom/example/jnitest/EnColor;");jfieldID j_fieldID_PURPLE = env->GetStaticFieldID(clazzColor,"PURPLE","Lcom/example/jnitest/EnColor;");jfieldID j_fieldID_YELLOW = env->GetStaticFieldID(clazzColor,"YELLOW","Lcom/example/jnitest/EnColor;");switch (color){case COLOR_RED:objColor = env->GetStaticObjectField(clazzColor,j_fieldID_RED);break;case COLOR_PURPLE:objColor = env->GetStaticObjectField(clazzColor,j_fieldID_PURPLE);break;case COLOR_YELLOW:objColor = env->GetStaticObjectField(clazzColor,j_fieldID_YELLOW);break;default:objColor = nullptr;}env->DeleteLocalRef(clazzFruit);env->DeleteLocalRef(clazzColor);return objColor;
}
- 还是通过 FindClass查找Java中代表颜色的类。
- 由于Java的enum成员都是静态的,我们通过GetStaticFieldID获取颜色类的静态成员ID。它的第一个参数是一个Java类;第二个参数是使用UTF-8编码的属性名;第三个参数是使用UTF-8编码的属性类型签名。
- 最后我们就可以通过类的GetStaticObjectField方法获取一个静态的成员,这个方法可以获得一个类的实例。他的第一个参数是一个Java类;第二个参数是这个静态成员的ID。
上面的方法虽然可行,但是在旁边的JNI二哥看不下去了,还没等大哥写完演算,就一把抢过来,在EnColor的枚举里自定义了一个static方法,获取指定顺序的枚举。改成了下面的样子。
public enum EnColor {RED,PURPLE,YELLOW,COLOR_MAX;public static EnColor getColor(int ordinal){EnColor colors[] = values();if(ordinal < colors.length && ordinal >= 0)return colors[ordinal];return null;}
}
对应的JNI如下修改,顿时清爽了很多。
extern "C" jobject JNICALL Java_com_example_jnitest_MainActivity_getFruitColor(JNIEnv *env, jobject object, jobject objFruit){jclass clazzFruit = env->FindClass("com/example/jnitest/EnFruit");jmethodID j_method_fruitOrdinal = env->GetMethodID(clazzFruit,"ordinal","()I");int fruit = env->CallIntMethod(objFruit,j_method_fruitOrdinal);COLOR color = get_fruit_color((FRUIT)fruit);//自定义水果颜色识别器,识别水果颜色jclass clazzColor = env->FindClass("com/example/jnitest/EnColor");jmethodID j_method_getColor = env->GetStaticMethodID(clazzColor,"getColor","(I)Lcom/example/jnitest/EnColor;");jobject objColor = env->CallStaticObjectMethod(clazzColor,j_method_getColor,(int)color);env->DeleteLocalRef(clazzFruit);env->DeleteLocalRef(clazzColor);return objColor;
}
- 修改之后我们通过GetStaticMethodID获得一个静态方法ID。该方法与GetMethodID方法的参数都是一样的。
- 然后我们通过CallStaticObjectMethod调用了一个我们自定义的静态方法,返回值是一个类的实例。CallStatic<type>Method这一类方法第一个参数是一个Java类,它不像Call<type>Method的方法那样需要类的实例;其余的参数和Call<type>Method方法是一样的。
这里有个小问题,我们在JNI里拿到的objColor并没有释放,不会造成内存泄漏的问题吗?首先,我们当然不能释放他,因为JAVA里还需要用到它。其次,Java是个好孩子,他最后会把从JNI里拿到的对象释放掉的,我们完全不用担心。
好了,现在应用可算知道识别器说的是哪个紫色了,赶紧把这个结果告诉我们老板的女儿。大功告成!
老板女儿:可是昨天我们吃的葡萄不是紫色的呀?爸爸你骗我,你骗我。(大哭)
老板(怒):你明天不用来上班了!
摔!!!
参考文献:
枚举的本质
java enum(枚举)使用详解 + 总结
C语言中的enum(枚举)用法
JNI完全指南(二)——类与异常
JNI完全指南(四)——对象操作
Java枚举和C枚举的转换——JNI笔记相关推荐
- java枚举类型转换_java枚举类型enum值与整数的转换
java编程中偶尔会用到枚举,为了直观,我们通常将枚举值设置为形象的单词,方便理解和使用.枚举类型相当于数据库 中字典表,但是枚举只有字典表的值,缺少其他用来表示和值一一对应的字段,当我们在数据库中保 ...
- Java枚举(用Java普通类模拟枚举的实现原理及JDK枚举API使用示例)
枚举的特点: ◆类型安全(Type Safety) ◆紧凑有效的枚举数值定义(Compact, Efficient Declaration of Enumerated Values) ◆无缝的和程 ...
- java集合,多线程,面向对象,枚举类,异常等所有常用的方法总和
extends //继承 Instanceof //多态里面的比较是否new 的对象为他 abstract //抽象类 final // 不可修改,最终的 如果在类前面加那么表示此类不可以被继承 st ...
- java枚举数字_Java枚举类型的使用,数值的二进制表示
一.Java枚举类型的使用 首先请看这段代码: packagejava上课;public classEnumTest {public static voidmain(String[] args) { ...
- java枚举 数字_java 枚举
Java中枚举的线程安全性及序列化问题 --枚举是如何保证线程安全的 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?答案很明显不是,enum就和class一样,只是一个关键字 ...
- Java OOP 8 实用类(枚举、包装类、日期类、Random类)
Java OOP 8 实用类 文章目录 Java OOP 8 实用类 一.学习目标 二.Java API 三.枚举 3.1为什么使用枚举? 3.2什么是枚举? 3.3如何使用枚举? 3.4常见错误 四 ...
- Java语言中的枚举类型
枚举类型(Enumerated Type)在编程语言中常用,程序员必备食粮,下面随着我的思路来认识一下枚举类型. 是什么 枚举类型在java中是一种基本数据类型.它用于声明一组命名的常数,当一个变量有 ...
- Java基础教程(15)--枚举类型
枚举类型定义了一个枚举值的列表,每个值是一个标识符.例如,下面的语句声明了一个枚举类型,用来表示星期的可能情况: public enum Day {SUNDAY, MONDAY, TUESDAY, ...
- 浅谈在Java开发中的枚举的作用和用法
2019独角兽企业重金招聘Python工程师标准>>> 在枚举出现之前,如果想要表示一组特定的离散值,往往使用一些常量.例如: [java] view plain copy pack ...
最新文章
- OpenVINO + OpenCV实现点头与摇头识别验证
- c语言练习题及答案)(1),c语言练习题(带详解答案)1.pdf
- atm c语言流程图_c语言ATM机程序
- 2009计算机网络考研大题,2009年计算机考研统考真题网络部分分析
- origin数据平滑_研发工程师必备:20条实用origin技能,让作图效率飞起来
- 上班之第一个无聊周末
- 提升开发效率的 Chrome 开发者工具快捷键参考
- 使用 imitator 实现前后端分离开发中的数据模拟与静态资源映射
- 形象解释 undefined 和 null 之间的区别 ​
- VFP DownFileX下载异常问题
- 网页监控系统设计之使用mjpg-streamer
- EasyDarwin源码分析(三)——rtsp-client
- 发改委交通运输部印发《推进“互联网+”便捷交通 促进智能交通发展的实施方案》(全文)...
- Win10编译和测试libModbus
- 直线回归和相关------(四)直线相关系数和决定系数(原理与公式推导)
- form表单提交方式
- 50 行代码爬取链家租房信息
- Android与ARM处理器
- 产品经理 demo html,18个UI demo设计实例,深挖让用户愉悦的小惊喜
- php制作日历带节日实验目的,php制作日历