【问题标题】:How to check the return type of a generic method to prevent NPE when unboxing拆箱时如何检查泛型方法的返回类型以防止NPE
【发布时间】:2016-05-04 18:48:05
【问题描述】:

考虑以下代码:

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。我该如何改进这段代码?

【问题讨论】:

  • get 不会发生拆箱。出现您的错误是因为 instanceof 的左侧需要是引用值表达式,而不是类型。使用o instanceof Integer。摆脱== nullinstanceof 已经针对 null 进行了验证。
  • 如果您尝试将get 的结果分配给原始变量,则会发生拆箱。
  • @Pillar 如果 o == null 则它不会是 instanceof Integer,因此该方法将返回 null 并在“int i = get...”这一行抛出 NPE
  • if (o instanceof Integer) 然后返回o。如果不是,则返回一些不是 null 的默认值,如果您想阻止 NPE。
  • 没有很好的方法来做到这一点。尽可能避免它。 codeaffine.com/2015/03/04/…

标签: java generics dictionary return-type autoboxing


【解决方案1】:

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

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

【讨论】:

  • 而在 Java 8 中,您可以使用 Map 接口的 getOrDefault 方法
【解决方案2】:

您可以使用包含类型信息和标签的键来执行此操作:

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 中的转换是安全的,因为您知道只有正确类型的条目才被放入映射中。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-12-24
    • 1970-01-01
    • 2023-03-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多