gpt4 book ai didi

java - 无法在 map 中设置通用属性的值

转载 作者:行者123 更新时间:2023-11-30 03:27:05 24 4
gpt4 key购买 nike

我正在尝试设置 map 中的属性值,但出现以下错误:

The method setValue(capture#7-of ?) in the type WritableValue<capture#7-of ?> is not applicable for the arguments (capture#8-of ?)

这是我的代码:

Map<String, Property<?>> map1 = new HashMap<String, Property<?>>();
Map<String, Property<?>> map2 = new HashMap<String, Property<?>>();

map1.put("key1", new SimpleIntegerProperty(5));
map1.put("key2", new SimpleStringProperty("hi")); //I need multiple property types in this Map, all of which implement Property

map2.put("key1", new SimpleIntegerProperty(5));
map2.put("key2", new SimpleStringProperty("hi"));

//I can assume that the value of the properties with the same key are of the same type
map2.get("key1").setValue(map1.get("key1").getValue()); //Error
map2.get("key2").setValue(map1.get("key2").getValue()); //Error

我不能这样做,它们必须仅复制值:

map2.put("key1", map1.get("key1"));
map2.put("key2", map1.get("key2"));

我可以在没有 map 的情况下进一步简化:

Property<?> p1 = new SimpleIntegerProperty(5);
Property<?> p2 = new SimpleIntegerProperty(10);
p1.setValue(p2.getValue());//Same (basic) error
p1.setValue(new Object());//Same (basic) error

我使用的是 Java 1.8 JDK

最佳答案

问题在于您尝试做的事情本质上不是类型安全的。从编程逻辑你可能知道 "key1" 关联的属性类型在map1"key1" 关联的属性类型相同在map2 ,但编译器不可能保证这一事实。因此,根据您当前的结构,您唯一的选择就是放弃编译时安全性。

这里的根本问题是Map有错误的 API 来满足您的要求(即使它的基本功能正是您所需要的)。 Map是一个同质容器,这意味着给定映射中的所有值都具有相同类型。这是由 API 强制执行的:public V get(K key);public void put(K key, V value);始终使用相同的类型 V ,对于任何单个 Map 都是固定的实例。您真正想要的是一个异构容器,其中的值根据键的不同而变化。所以您需要一个 API,其中 V对于容器的实例来说不是固定的,但是每次调用方法 get 时都会发生变化和put ,取决于键的值。所以你需要 get 的东西和put方法是通用方法:

public interface Container<K> { // K is the type of the key...

public <V> V get(K key) ;
public <V> void put(K key, V value);

}

这种实现记录在 Josh Bloch 的“Effective Java”中,称为“类型安全异构容器”模式。

首先为您的键定义一个类型,该类型维护相应属性的类型:

    /**
* @param <T> The type associated with this key
* @param <K> The actual type of the key itself
*/
public class TypedKey<T, K> {
private final Class<T> type ;
private final K key ;

public TypedKey(Class<T> type, K key) {
if (type == null || key == null) {
throw new NullPointerException("type and key must be non-null");
}
this.type = type ;
this.key = key ;
}

@Override
public boolean equals(Object o) {
if (o == null) return false ;
if (! (o instanceof TypedKey)) {
return false ;
}
TypedKey<?, ?> other = (TypedKey<?, ?>) o ;
return other.type.equals(type) && other.key.equals(key);
}

@Override
public int hashCode() {
return Objects.hash(type, key);
}
}

这里T将是属性的类型,并且 K是 key 的实际类型。所以你将修改你的代码为

// String key to map to Property<Number>:
TypedKey<Number, String> key1 = new TypedKey<>(Number.class, "key1");

// String key to map to Property<String>:
TypedKey<String, String> key2 = new TypedKey<>(String.class, "key2");

现在定义一个容器类来充本地图。这里的基本思想是有两种方法:

public <T> void put(TypedKey<T, K> key, Property<T> property)

public <T> Property<T> get(TypedKey<T, K> key)

实现非常简单:

    /**
* @param <K> The type of the key in the TypedKey
*/
public class TypedPropertyMap<K> {
private final Map<TypedKey<?, K>, Property<?>> map = new HashMap<>();

public <T> void put(TypedKey<T, K> key, Property<T> property) {
map.put(key, property);
}

@SuppressWarnings("unchecked")
public <T> Property<T> get(TypedKey<T, K> key) {

// by virtue of the API we defined, the property associated with
// key must be a Property<T> (even though the underlying map does not know it):

return (Property<T>) map.get(key);
}
}

这里有一些微妙之处。因为底层 map 是private ,我们确信访问它的唯一方法是通过我们的 putget方法。因此,当我们调用get()时在底层 map 上带有 TypedKey<T,K> ,我们确信相应的属性必须是(null或)Property<T> (因为这是编译器唯一允许提前插入的内容)。因此,即使编译器没有意识到这一点,我们也能保证强制转换成功,并且到 @SuppressWarnings是有道理的。

现在如果我创建一个TypedPropertyMap<K> ( K 这里只是实际 key 的类型),我在编译时保证 map.get(key1)返回 Property<Number> (因为 key1 的编译时类型为 TypedKey<Number, String> )和 map.get(key2)返回 Property<String> (因为 key2 的编译时类型为 TypedKey<String, String> )。

这是一个完整的可运行示例:

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

import javafx.beans.property.Property;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.beans.property.SimpleStringProperty;


public class TypedPropertyMapTest {

public static void main(String[] args) {
TypedPropertyMap<String> map1 = new TypedPropertyMap<>();
TypedPropertyMap<String> map2 = new TypedPropertyMap<>();

TypedKey<Number, String> key1 = new TypedKey<>(Number.class, "key1");
TypedKey<String, String> key2 = new TypedKey<>(String.class, "key2");

map1.put(key1, new SimpleIntegerProperty(5));
map1.put(key2, new SimpleStringProperty("hi"));

map2.put(key1, new SimpleIntegerProperty());
map2.put(key2, new SimpleStringProperty());

map2.get(key1).setValue(map1.get(key1).getValue());
map2.get(key2).setValue(map1.get(key2).getValue());

System.out.println(map2.get(key1).getValue());
System.out.println(map2.get(key2).getValue());

}


/**
* @param <T> The type associated with this key
* @param <K> The actual type of the key itself
*/
public static class TypedKey<T, K> {
private final Class<T> type ;
private final K key ;

public TypedKey(Class<T> type, K key) {
if (type == null || key == null) {
throw new NullPointerException("type and key must be non-null");
}
this.type = type ;
this.key = key ;
}

@Override
public boolean equals(Object o) {
if (o == null) return false ;
if (! (o instanceof TypedKey)) {
return false ;
}
TypedKey<?, ?> other = (TypedKey<?, ?>) o ;
return other.type.equals(type) && other.key.equals(key);
}

@Override
public int hashCode() {
return Objects.hash(type, key);
}
}

/**
* @param <K> The type of the key in the TypedKey
*/
public static class TypedPropertyMap<K> {
private final Map<TypedKey<?, K>, Property<?>> map = new HashMap<>();

public <T> void put(TypedKey<T, K> key, Property<T> property) {
map.put(key, property);
}

@SuppressWarnings("unchecked")
public <T> Property<T> get(TypedKey<T, K> key) {

// by virtue of the API we defined, the property associated with
// key must be a Property<T> (even though the underlying map does not know it):

return (Property<T>) map.get(key);
}
}
}

请注意,将其设为 ObservableMap 确实很难,出于与简介中概述的相同原因:ObservableMap接口(interface)定义了同类方法(实际上是从 Map 接口(interface)继承的),您无法以满足您的要求的方式实现这些方法。但是,您可以轻松制作此工具 javafx.beans.Observable ,这将允许您注册 InvalidationListener s 与它,并在绑定(bind)中使用它:

   public class TypedPropertyMap<K> implements Observable { 
private final ObservableMap<TypedKey<?, K>, Property<?>> map = FXCollections.observableHashMap();

@Override
public void addListener(InvalidationListener listener) {
map.addListener(listener);
}

@Override
public void removeListener(InvalidationListener listener) {
map.removeListener(listener);
}

// remaining code as before...
}

关于java - 无法在 map 中设置通用属性的值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29948279/

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