gpt4 book ai didi

java - 避免在具有多种值类型的映射中进行未经检查的赋值?

转载 作者:IT老高 更新时间:2023-10-28 21:15:09 29 4
gpt4 key购买 nike

我在 Java 7 中遇到警告问题:

Unchecked assignment: 'java.lang.Class' to 'java.lang.Class<T>'

我正在上线 Class<T> type = typeMap.get(key);在下面的get函数中。

基本上我在这里尝试做的是我想存储一堆未知类型的键/值对(但除了 null 之外,它们都是 Object 的后代),但不会丢失类型。所以我使用泛型创建了一个具有以下内容的类。它有两个映射(一个用于存储数据,一个用于存储类类型:

    private Map<String, Object>  dataMap = new HashMap<>();
private Map<String, Class> typeMap = new HashMap<>();

public <T> void put(String key, T instance){
dataMap.put(key, instance);
if (instance == null){
typeMap.put(key,null);
}
else {
typeMap.put(key, instance.getClass());
}
}

public <T> T get(String key){
Class<T> type = typeMap.get(key);
if (type == null){
return null;
}
return type.cast(dataMap.get(key));
}

它运行得很好,但警告让我很烦。有什么方法可以让 Java 在不提示的情况下进行此转换(除了压制它)?还是有更好的方法来完成我想做的事情? Java 8 怎么样,因为我还没有机会深入研究它?

谢谢!

最佳答案

您所显示的内容不安全的原因是这个作业:

Class<T> type = typeMap.get(key);

T 不需要与从 map 中检索到的 Class 有任何关系。 T 总是从调用 get 的周围上下文中推断出来的。例如,我可以执行以下调用序列:

// T is inferred from the arguments as String (which is fine)
example.put("k", "v");
// T is inferred from the return value target type as Integer
Integer i = example.get("k");

get 方法中, String.class 从类型映射中正确检索,但未经检查地转换为 Class<Integer> 。对 type.cast(...) 的调用不会抛出,因为从数据映射中检索到的值是 String实际上发生了隐式检查转换,返回值,将其转换为 Integer 并抛出 ClassCastException

这种奇怪的交互是由于 type erasure 造成的。


因此,当我们在单个数据结构中存储多种类型时,有多种方法可以处理它,具体取决于我们的需求。

1。如果没有办法执行它们,我们可以放弃编译检查。

在这里存储 Class 大部分是没有意义的,因为正如我上面所展示的,它不会执行有用的验证。所以我们可以按照以下思路重新设计 map :

class Example {
private final Map<String, Object> m = new HashMap<>();

void put(String k, Object v) {
m.put(k, v);
}

Object getExplicit(String k) {
return m.get(k);
}

@SuppressWarnings("unchecked")
<T> T getImplicit(String k) {
return (T) m.get(k);
}
}

getExplicitgetImplicit 做类似的事情,但是:

String a = (String) example.getExplicit("k");
// the generic version allows an implicit cast to be made
// (this is essentially what you're already doing)
String b = example.getImplicit("k");

在这两种情况下,我们只是依靠自己作为程序员的意识来不犯错误。

抑制警告不一定是坏事,重要的是要了解它们的实际含义和含义。

2。将 Class 传递给 get,因此返回值必须有效。

这是我看到的典型做法。

class Example {
private final Map<String, Object> m = new HashMap<>();

void put(String k, Object v) {
m.put(k, v);
}

<T> T get(String k, Class<T> c) {
Object v = m.get(k);
return c.isInstance(v) ? c.cast(v) : null;
}
}

example.put("k", "v");
// returns "v"
String s = example.get("k", String.class);
// returns null
Double d = example.get("k", Double.class);

但是,当然,这意味着我们需要向 get 传递两个参数。

3。参数化键。

这是一本小说,但更先进,它可能更方便,也可能不更方便。

class Example {
private final Map<Key<?>, Object> m = new HashMap<>();

<V> Key<V> put(String s, V v) {
Key<V> k = new Key<>(s, v);
put(k, v);
return k;
}

<V> void put(Key<V> k, V v) {
m.put(k, v);
}

<V> V get(Key<V> k) {
Object v = m.get(k);
return k.c.isInstance(v) ? k.c.cast(v) : null;
}

static final class Key<V> {
private final String k;
private final Class<? extends V> c;

@SuppressWarnings("unchecked")
Key(String k, V v) {
// this cast will always be safe unless
// the outside world is doing something fishy
// like using raw types
this(k, (Class<? extends V>) v.getClass());
}

Key(String k, Class<? extends V> c) {
this.k = k;
this.c = c;
}

@Override
public int hashCode() {
return k.hashCode();
}

@Override
public boolean equals(Object o) {
return (o instanceof Key<?>) && ((Key<?>) o).k.equals(k);
}
}
}

例如:

Key<Float> k = example.put("k", 1.0f);
// returns 1.0f
Float f = example.get(k);
// returns null
Double d = example.get(new Key<>("k", Double.class));

如果条目是已知的或可预测的,这可能是有意义的,因此我们可以有类似的东西:

final class Keys {
private Keys() {}
static final Key<Foo> FOO = new Key<>("foo", Foo.class);
static final Key<Bar> BAR = new Key<>("bar", Bar.class);
}

然后我们不必在检索完成的任何时候构造一个关键对象。这非常有效,特别是在为字符串类型的场景添加一些强类型时。

Foo foo = example.get(Keys.FOO);

4。没有 map 可以放入任何类型的对象,请使用某种多态性。

如果可能且不太麻烦,这是一个不错的选择。如果存在使用不同类型的共同行为,请将其设为接口(interface)或父类(super class),这样我们就不必使用强制转换。

一个简单的例子可能是这样的:

// bunch of stuff
Map<String, Object> map = ...;

// store some data
map.put("abc", 123L);
map.put("def", 456D);

// wait awhile
awhile();

// some time later, consume the data
// being particular about types
consumeLong((Long) map.remove("abc"));
consumeDouble((Double) map.remove("def"));

我们可以改为这样:

Map<String, Runnable> map = ...;

// store operations as well as data
// while we know what the types are
map.put("abc", () -> consumeLong(123L));
map.put("def", () -> consumeDouble(456D));

awhile();

// consume, but no longer particular about types
map.remove("abc").run();
map.remove("def").run();

关于java - 避免在具有多种值类型的映射中进行未经检查的赋值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22467645/

29 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com