一种Java通用的FeatureMap存取设计

时间:2021-04-04 20:19:37

一、map存取任意对象

  首先,定义map对象,将map的value类型指定为Object,它可以存储任意基础类型或自定义类型的值对象。由于值类型为Object,在读取map元素时,往往需要将值强制转换为需要的类型。其实,可以返回值类型为范型优化代码,将类型转换前置到get方法中,如下:

/**
* Created by Jerry on 17/7/22.
*/

public class CoMap {

private Map<String, Object> coMap = new HashMap<String, Object>();

public void putObj(String key, Object value){
coMap.put(key, value);
}

public <T> T getObj(String key) {
try {
return (T) coMap.get(key);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

CoMap存取任意对象的测试代码如下:

/**
* Created by Jerry on 17/7/19.
*/

public class Test {

private String field1;

private Integer field2;

Test(String field1, Integer field2) {
this.field1 = field1;
this.field2 = field2;
}

public String getField1() {
return field1;
}

public void setField1(String field1) {
this.field1 = field1;
}

public Integer getField2() {
return field2;
}

public void setField2(Integer field2) {
this.field2 = field2;
}

public static void main(String [] args) {
CoMap coMap = new CoMap();

coMap.putObj("a", 123);
coMap.putObj("b", 123.456);
coMap.putObj("c", "aaa");
coMap.putObj("d", Lists.newArrayList(1, 2, 3));
coMap.putObj("e", new String [] {"mmm", "nnn", "kkk"});
coMap.putObj("f", Sets.newHashSet("yyyy", "mm", "dd"));
coMap.putObj("g", new Test("test", 123));

Integer a = coMap.getObj("a");
System.out.println(a);
Double b = coMap.getObj("b");
System.out.println(b);
String c = coMap.getObj("c");
System.out.println(c);
List d = coMap.getObj("d");
System.out.println(JSON.toJSONString(d));
String [] e = coMap.getObj("e");
System.out.println(JSON.toJSONString(e));
Set f = coMap.getObj("f");
System.out.println(JSON.toJSONString(f));
Test g = coMap.getObj("g");
System.out.println(JSON.toJSONString(g));
}
}

结果:
123
123.456
aaa
[1,2,3]
["mmm","nnn","kkk"]
["yyyy","mm","dd"]
{"field1":"test","field2":123}
  • 说明
    • 1.CoMap对键值(key)没有进行良好封装,增加了数据读取的困难度
    • 2.CoMap没有对返回值(value)进行类型判断,这可能在数据强制类型转换时出现“java.lang.ClassCastException”异常
    • 3.getObj(String key)方法虽然有try-catch块,但却无法捕获数据强制转换导致的“java.lang.ClassCastException”异常

二、通用FeatureMap存取设计

  基于以上CoMap存在的问题,对该CoMap类进行优化,主要在两方面:1.对map键值进行封装;2.对返回值进行类型校验。该设计可用于通用FeatureMap的存取。

  • 2.1 key封装

      定义Field类,用于封装key,该处主要针对数据库Feature存储,所以将key定义为整形,valueClass用于在输入时做数据类型校验,保证存储的数据为Field指定类型。

//**
* Created by Jerry on 17/7/19.
*/
public class Field implements Serializable {
private static final long serialVersionUID = 2025270001756934102L;
private int key;
private Class valueClass;

Field(int key, Class valueClass) {
this.key = key;
this.valueClass = valueClass;
}

public String getKeyStr() {
return String.valueOf(this.key);
}

public int getKey() {
return this.key;
}

public Class getValueClass() {
return valueClass;
}
}

  属性Field定义后,可进行Schema定义,Schema中约定最终用于数据存取的key,Schema的定义采用单例实现。

/**
* Created by Jerry on 17/7/23.
*/

public class TestSchema {

private Field feature1;

private Field feature2;

private Field feature3;

private Field feature4;

private static final TestSchema INSTANCE = new TestSchema();

private TestSchema() {
feature1 = new Field(1, Date.class);
feature2 = new Field(2, Long.class);
feature3 = new Field(3, String.class);
feature4 = new Field(4, Test.class);
}

public static TestSchema getInstance() {
return INSTANCE;
}

public Field getFeature1() {
return feature1;
}

public Field getFeature2() {
return feature2;
}

public Field getFeature3() {
return feature3;
}

public Field getFeature4() {
return feature4;
}
}
  • 2.2 存取数据校验

      第一部分在getObj方法中有捕获数据转换错误的代码块,但代码实际执行过程中,try-catch块无法捕获该处的“java.lang.ClassCastException”异常,这是因为在getObj方法中,范型的类型已经被擦除,该处的强制转换可以避免编译错误,但数据类型的真正转换却不发生在该处,而是在给调用方返回赋值的时候,因此第一部分测试代码如果返回值类型指定错误仍然会抛出异常。
      对getObj和getField方法进行优化,将返回值类型的类信息作为参数,在数据返回前对返回值类型进行校验。
      此外,对setField方法也进行优化,校验存储数据是否为Field指定数据类型。

/**
* Created by Jerry on 17/7/22.
*/

public class CoMap {

private Map<String, Object> coMap = new HashMap<String, Object>();

public void putObj(String key, Object value){
coMap.put(key, value);
}

public <T> T getObj(String key, Class<T> clazz) {
if (clazz.isInstance(coMap.get(key)) == false) {
System.out.println("return type is wrong");
return null;
}
return (T) coMap.get(key);
}

public <T> void setField(Field field, T value) {
// 校验输入数据类型是否为field指定数据类型
if(field.getValueClass().isInstance(value) == false) {
System.out.println("value type is wrong");
return ;
}

coMap.put(field.getKeyStr(), value);
}

/**
* 获取数据,如果数据类型与返回值类型不匹配,则:ClassCastException,该异常在会在调用方赋值时抛出,无法在方法内捕获
* @param field
* @param <T>
* @return
*/

public <T> T getField(Field field) {
// try catch 无法捕获类型转换异常,因为类型已擦除
return (T) coMap.get(field.getKeyStr());
}

/**
* 获取数据,校验数据类型与返回值类型是否匹配
* @param field
* @param clazz
* @param <T>
* @return
*/

public <T> T getField(Field field, Class<T> clazz) {
if (clazz.isInstance(coMap.get(field.getKeyStr())) == false) {
System.out.println("return type is wrong");
return null;
}
return (T) coMap.get(field.getKeyStr());
}
}

测试代码如下:

/**
* Created by Jerry on 17/7/19.
*/

public class Test {

private String field1;

private Integer field2;

Test(String field1, Integer field2) {
this.field1 = field1;
this.field2 = field2;
}

public String getField1() {
return field1;
}

public void setField1(String field1) {
this.field1 = field1;
}

public Integer getField2() {
return field2;
}

public void setField2(Integer field2) {
this.field2 = field2;
}

public static void main(String [] args) {
CoMap coMap = new CoMap();

coMap.setField(TestSchema.getInstance().getFeature1(), 123);
coMap.setField(TestSchema.getInstance().getFeature1(), new Date());
coMap.setField(TestSchema.getInstance().getFeature2(), 123L);
coMap.setField(TestSchema.getInstance().getFeature3(), "abc");
coMap.setField(TestSchema.getInstance().getFeature4(), new Test("test", 123));

Date f1 = coMap.getField(TestSchema.getInstance().getFeature1(), Date.class);
System.out.println(f1);
Long f2 = coMap.getField(TestSchema.getInstance().getFeature2(), Long.class);
System.out.println(f2);
String f3 = coMap.getField(TestSchema.getInstance().getFeature3(), String.class);
System.out.println(f3);
Test f4 = coMap.getField(TestSchema.getInstance().getFeature4(), Test.class);
System.out.println(JSON.toJSONString(f4));
String f4_1 = coMap.getField(TestSchema.getInstance().getFeature4(), String.class);
}
}


结果:
value type is wrong
Sun Jul 23 16:39:59 CST 2017
123
abc
{"field1":"test","field2":123}
return type is wrong