使用TypeSafeMap强制编译类型安全性



问题陈述:我们正在构建一个以TypeSafeMap作为响应的库。TypeSafeMap是一个可以容纳任何类型对象的映射。现在,客户端将访问typesafemap。我们正在尝试强制执行某种级别的编译类型安全性。以下是代码和更多解释。

响应结构:

public Class Response {
private TypeSafeMap t;

public TypeSafeMap getMap() { return t; }
}

//类型安全地图

public class TypeSafeMap 
{
private final static Map<String, Object> map = new HashMap<>();

public static <T> T put(String key, T value) {
if (null != key) {
return (T) map.put(key, value);
}
return (T) map;
}
@SuppressWarnings("unchecked")
public static <T> T get(PartyEnums partyEnum)
{       
return (T) map.get(partyEnum.PARTY.name());
}
}

//我们向客户端公开的枚举,以获取属性和相应的字段类型

public enum PartyEnums
{   
PARTY("party", new ArrayList<Party>().getClass()); 

private final String name;
private final Class<?> clzz; //this is the type client should access as field type
PartyEnums(String name,Class<?> clzz) 
{
this.name = name;
this.clzz=clzz;
}

public Class<?> getClzz()
{
return clzz;
}

@SuppressWarnings("unchecked")
public <T> T getInstance()
{
T ins = null;
try {
ins = (T) getClzz().newInstance();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return ins;
}
}

//客户端呼叫

public class ClientCall {
Object obj = TypeSafeMap.get(PartyEnums.PARTY); //No error. 
String str = TypeSafeMap.get(PartyEnums.PARTY); //No error. 
But we want enforce some level of compile type safety as the field type "str" and TypeSafeMap.get() type do not match. 
How can we enforce compile type safety?

List<Party> party = TypeSafeMap.get(PartyEnums.PARTY);// OK.
}

TL;DR

使用当前形式的enum无法完成此操作。有一个JEP 301:增强枚举可以解决同样的问题,但它已经被撤回了。


您(目前(强制类型安全的唯一选择是通过使用具有预定义类型常量的final类来模拟enum。我想它是这样的:

public final class Key<V> {
public static final Key<List<Party>> PARTY_LIST = new Key<>("party list");
public static final Key<Map<Integer, String>> PARTY_MAP = new Key<>("party map");
public static final Key<String> SOME_STRING = new Key<>("party string");
private final String name;

private Key(String name) {
this.name = name;
}

public String getName() {
return name;
}
}

现在,我们已经准备好通过以下方式修改<T> T put(String key, T value)(即使"枚举方法"在理论上是可能的,这显然也不安全(和<T> T get(PartyEnums partyEnum),使您的TypeSafeMap真正类型安全:

public static class TypeSafeMap {
private final static Map<String, Object> MAP = new HashMap<>();
@SuppressWarnings("unchecked")
public static <T> T put(Key<T> key, T value) {
if (null != key) {
return (T) MAP.put(key.getName(), value);
}

return value;
}
@SuppressWarnings("unchecked")
public static <T> T get(Key<T> key) {
return (T) MAP.get(key.getName());
}
}

我仍然看到至少两个可能的问题:

  1. 多个常量可以共享同一个name,因此一旦使用,就可以有效地覆盖彼此(例如,可以使用enum而不是String(
  2. 即使对于null键,put(Key<T> key, T value)也会通过输入value返回,这(在我看来(是故障安全的,但对调用方(他可能认为"存储"操作以某种方式成功了(具有误导性

然而,鉴于上述实现的弱点仍然符合您的初衷,因此:

这些案例会通过:

map.put(Key.JUST_STRING, "just string");
map.put(Key.PARTY_LIST, Arrays.asList(new Party()));
...
Object resultAsObject = map.get(Key.PARTY_LIST);
List<Party> resultAsList = map.get(Key.PARTY_LIST);
String resultString = map.get(Key.JUST_STRING);

但这些失败与编译时错误:

map.put(Key.PARTY_LIST, Arrays.asList(new String()));
...
String stringFromList = map.get(Key.PARTY_LIST);

请注意,Object resultAsObject = map.get(<ANYTHING>)将始终成功,因为任何返回值(包括null(都可以表示为Object变量

最新更新