如何在取消装箱时检查泛型方法的返回类型以防止NPE



考虑以下代码:

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
 *
 * @author Colby
 */
public class Entity {
    //test code
    public static void main(String[] args) {
        Entity e = new Entity();
        e.put("username", "colby");
        e.put("level", 99);
        e.put("hours played", Long.MAX_VALUE - 1);
        e.put("banned", true);
        String username = e.get("username");
        int level = e.get("level");
        long played = e.get("hours played");
        boolean banned = e.get("banned");
        System.out.println(username);
        System.out.println(level);
        System.out.println(played);
        System.out.println(banned);
    }
    public Entity() {
        map = new ConcurrentHashMap<>();
    }
    private final Map<String, Object> map;
    public <T> void put(String key, T value) {
        map.put(key, value);
    }
    public <T> T get(String key) {
        return (T) map.get(key);
    }
    public boolean has(String key) {
        return get(key) != null;
    }
}

一个非常有用的属性系统。现在,当我做这样的事情时,我的问题来了:

int i = e.get("non existent key");

map get将返回null,因此将在开箱时抛出一个NPE。

我尝试的解决方案是:

public <T> T get(String key) {
    Object o = map.get(key);
    if(o == null) {
        if(T instanceof Integer) {
            return (T) new Integer(-1);
        }
    }
    return (T) o;
}

然而,我得到一个错误,无法在instanceof线上找到符号T。如何改进此代码?

您可以将默认值作为参数传递给方法。

public <T> T get(String key, T defaultValue) {
    Object o = map.get(key);
    if(o == null) {
        return defaultValue;
    } else {
        return (T) o;
    }
}

您可以使用包含类型信息和标签的密钥来完成此操作:

abstract class Key<T> {
  private final String label;
  private final T defaultValue;
  Key(String label, T defaultValue) {
    // Assign to fields.
  }
  T defaultValue() { return defaultValue; }
  @Override public boolean equals(Object obj) {
    // Implement to check both label and getClass().
  }
  @Override public int hashCode() {
    // Implement to check both label and getClass().
  }
}

然后使用这个而不是普通的String:

public <T> void put(Key<T> key, T value) {
  map.put(key, value);
}
public <T> T get(Key<T> key) {
  if (!map.containsKey(key)) {
    return key.defaultValue();
  } else {
    return (T) map.get(key);
  }
}

然后实例化Key实例,如:

Key<String> fooKey = new Key<String>("Foo", "someDefaultValue") {};
Key<Integer> barKey = new Key<Integer>("Bar", -1) {};
Key<List<String>> listKey = new Key<List<String>>("List", new ArrayList<String>()) {};

这允许您将客户端对与密钥相关联的类型的知识进行编码。get中的强制转换是安全的,因为您知道只有正确类型的条目才被放入映射中。

最新更新