举个例子:GUI上有个下拉框里面有"特级","高级","中级","初级"这几个选项,当选中之后会处理一些事情。但具体的代码里一般不会设置为字符串类型,而是int或者枚举类型。如下:
public class LvBean {
private int lv;//0 特级 1高级 2中级 3低级
public int getLv() {
return lv;
}
public void setLv(int lv) {
this.lv = lv;
}
}
这样的情况如果放在浏览器里则可以用<select><option>的name,value来表示即可,放到java代码里则肯定需要另外处理的,比如:
if (value == "特级"){
}else if...
...
...
因为这种情况的普遍性,所以实现接口是不错的,下面是一个接口方法:根据字符串返回真正的值。
T getFromAsText(String text) ;
我们可以写实现这个接口,然后在里面去判断,如下:
@Override
int getFromAsText(String text) {
if (value == "特级"){
return 0;
}else if...
...
...
}
很不错!!!但前辈们远远比我们想的多。
他们早就这样做了,且做的更好。
这就是PropertyEditor。
从字面上来说,就是属性编辑器。
这是一个接口,它是属于java.beans包的。
这个接口创造的目的是为了GUI而创造的。但它也可以做其他的一些事情。
它最重要的是这几个方法:
void setValue(Object value);//设置值
Object getValue();//获得值
void setAsText(String text) throws java.lang.IllegalArgumentException;//根据字符串设置值
setAsText这个方法就是将字符串转换成其他值,根据自己需要去实现它,可以转成日期啊等等。
光有PropertyEditor接口的话还是比较懵逼的,毕竟是void方法,即使实现了该接口,那么set到哪里啊?
其他它有个基本实现类PropertyEditorSupport,算是一个强适配器吧。
里面的代码非常简单。
你会发现它里面维护了几个Object变量,所有的操作都是围绕着这几个变量来的,这下就清晰很多了。(当你读到这里的时候,请停下脚步查看PropertyEditorSupport源码)
实际上我们可以不去直接实现PropertyEditor的,而是直接继承自PropertyEditorSupport,根据上面的例子,修改setAsText,如下代码:
public class LvPropertyEdioter extends PropertyEditorSupport{
private static String [] lvs = {"特级","高级","中级","初级"};
@Override
public void setAsText(String text) throws IllegalArgumentException {
for (int i = 0; i < lvs.length; i++) {
if (lvs[i].equals(text)){
setValue(i);
break;
}
}
}
}
这个会把传来的字符串比较,如果相同,把索引set到Object变量里。那么取的时候直接getValue的。
如果只是实现接口来处理这种转换工作的话,代码还不够优秀,我们可以更进一步。
我们设想一下,如果我们做了类似于这样JSON配置文件:
[
{
className:LvBean.class,
fieldName:"lv",
fieldEdioter:LvPropertyEdioter.class
},{
...
}
...
]
到时候直接反射操作该多爽啊?
在持续开发的过程中,配置以及修改配置是很不如意的事情。
我们可以通过实例对象BeanInfo来这样做:
BeanInfo也是接口,这个接口就是指Bean的信息。包括什么类的什么属性,使用的什么PropertyEdioter。
它有个适配器:SimpleBeanInfo,我们直接继承它即可。
注意一件小事,听说实现BeanInfo的时候命名规则是要在Bean类后面加上BeanInfo这样的命名方式。如下:
public class LvBeanBeanInfo extends SimpleBeanInfo{
@Override
public PropertyDescriptor[] getPropertyDescriptors() {
PropertyDescriptor pd = null;
try {
pd = new PropertyDescriptor("lv", LvBean.class);
pd.setPropertyEditorClass(LvPropertyEdioter.class);
} catch (IntrospectionException e) {
e.printStackTrace();
}
return new PropertyDescriptor[]{pd};
}
}
重写属性描述器方法。把Bean类,属性以及edioter类关联到一起。
来写个测试吧。
/**
* 需求:面板选中“高级”,处理一些事物
* @author dmw
*
* 2018年12月4日
*/
public class Test {
public static void main(String[] args) throws Exception {
String fieldName = "lv";//等级
String fieldTextVal = "中级";//内容
Class<LvBeanBeanInfo> clazz = LvBeanBeanInfo.class;//bean的配置信息对象
choose(fieldName,fieldTextVal,clazz);
}
/**
* 选中
*/
private static void choose(String fieldName,String fieldTextVal,Class<? extends BeanInfo> clazz) throws Exception {
PropertyEditor editor = getPropertyEditor(fieldName,clazz);
if (editor == null){
throw new NullPointerException();
}
//属性编辑器来转换字符串
editor.setAsText(fieldTextVal);
Object value = editor.getValue();
//进一步处理
handleLv(value);
}
/**
* 获取属性编辑器
*
*/
private static PropertyEditor getPropertyEditor(String fieldName,Class<? extends BeanInfo> clazz) throws Exception {
BeanInfo Info = clazz.newInstance();
PropertyDescriptor[] descriptors = Info.getPropertyDescriptors();
for (PropertyDescriptor propertyDescriptor : descriptors) {
//如果里面有属性名相同的,那么就直接返回附带的属性编辑器对象
if (propertyDescriptor.getName().equals(fieldName)){
Class<? extends PropertyEditor> editorClass = (Class<? extends PropertyEditor>) propertyDescriptor.getPropertyEditorClass();
return editorClass.newInstance();
}
}
return null;
}
/**
* 进一步处理
* @param value
*/
private static void handleLv(Object value) {
System.out.println(value);
//可以通过反射再赋值给LvBean
}
}
运行还不错。
结语:
通过模块解耦的方式来转换字符串类型只是PropertyEditor的一个功能而已。
spring写了很多类来实现它,有将字符串转成int的,字符串转double的,字符串转class的等等等等。这些editor既可用于xml解析注入对象属性,又可以用于http接参数到Controller类的方法参数里面。(都是字符串转其他类型啊)
BeanInfo里面还有其他的一些方法,包括事件的处理。