通用对象的集合。自动铸造?隐藏铸造?

Collection of Generic Objects. Auto-Casting? Hidden Casting?

我正在尝试以下列方式保存节点属性(这可能本身就是错误的):

public class Property<T> {
    protected String key;
    protected T value;

    public Property(String key, T value) {
        this.key = key;
        this.value = value;
    }
}


public class Node {
    protected HashMap<String,Property> properties;

    public Node() {
        properties = new HashMap<>();
    }

然而,这有一个不幸的副作用,那就是让我的铸造一团糟。我一直在阅读所有可能相关的内容,但似乎没有任何内容可以解决根本问题。这是我目前的想法:

    public void add(String key, Object value) {
        if ( ! (value instanceof Property)) {
            value = new Property<>(key, value);
        }
        properties.put(key, (Property)value);
    }

    public long get(String key, long x) {
        return (long)properties.get(key).value;
    }
    public long[] get(String key, long[] x) {
        return (long[])properties.get(key).value;
    }
    public String get(String key, String x) {
        return (String)properties.get(key).value;
    }
    // etc

现在这显然非常愚蠢,但我正在兜圈子试图简单地通过键获取节点 属性,并确保它是基于键的类型。

就这么简单。 给定的键必须对应给定的类型,用于添加和获取

老实说,我觉得我对 Java 的本质有一些基本的误解。

试试这个

// a node representing things of type T
public class Node<T> {
    protected HashMap<String,Property<T>> properties;

    public Node() {
        properties = new HashMap<>();
    }

    // add a T to the map
    public void add(String key, T value) {
        properties.put(key, new Property<T>(string, value));
    }
}

到目前为止,我对您的示例感到担忧的是 "Node" 看起来很像散列图条目。更好的问题是 "what are you REALLY trying to do"?

A given key must correspond to a given type, both for adding and for getting.

假设您的意思是 String key 是元素 及其类型 的标识符,那么您就不走运了,这对泛型来说根本不可能。一种选择是为每个已知 属性.

定义一个具有适当类型 field/getters 的自定义 class

如果你的意思是 x 参数,那么你可以使用泛型来做类似

public <T> T get(String key, T x) {
    return (T) properties.get(key).value;
}

但这会让你为各种 ClassCastException 做好准备。您的编译器应该对此发出警告。 (另请注意,您将无法直接使用原始类型。)

可以为此编写一个外部类型安全的实现,尽管它需要一些编译器无法证明正确的内部转换。

class TypeSafeMap {
  public static final class Key<T> {
    // deliberately empty; we're knowingly using reference equality
  }

  private final Map<Key<?>, Object> map;

  TypeSafeMap() {
    this.map = new HashMap<>();
  }

  public <T> T get(Key<T> key) {
    return (T) map.get(key); // cast is safe, but the compiler can't prove it
  }

  public <T> void put(Key<T> key, T value) {
    map.put(key, value);
  }
}

class SomewhereElse {
  static final Key<Integer> myIntKey = new Key<Integer>();
  static final Key<String> myStringKey = new Key<String>();

  public void doWhatever(TypeSafeMap myMap) {
    int myInt = myMap.get(myIntKey);
    String myString = myMap.get(myStringKey);
  }
}

...就是说,如果您事先知道整组键,您可以(并且应该)使用适当键入的字段进行自定义 class,而不是试图将整个内容压缩到类似地图的结构。

由于节点 class 可以保存任何值类型的属性,因此您无论如何都需要进行 unchecked cast。无需重载 get 函数,您可以 转换为任何预期的 return 类型 :

@SuppressWarnings("unchecked")
public <T> T get(String key) {
    return (T) properties.get(key).value;
}

示例:

Node node = new Node();
node.add("x", 123);
node.add("y", "ABC");
node.add("z", new Date());

int    valueX = node.get("x"); // cast to integer and autobox to int
String valueY = node.get("y"); // cast to String
Date   valueZ = node.get("z"); // cast to Date

String valueFail = node.get("z"); // this will throw a ClassCastException
public class Node
{
    public static void main (String[] args)
    {
        Node node = new Node();
        node.addProperty("a", 12L);
        node.addProperty("b", "i'm a string");

        long number = node.getProperty("a");
        String string = node.getProperty("b");
    }

    private Map<String, Object> properties = new HashMap<>();

    public void addProperty(String key, Object value){
        this.properties.put(key, value);
    }

    public <T> T getProperty(String key){
        return (T) this.properties.get(key);
    }
}

OP 正在尝试处理不同对象的集合,因此泛型不是前进的方向。他试图做的是对集合中的每个特定对象进行类型安全处理。以下是使用访问者模式可以做到这一点的方法。

// Implement this interface in something which needs to process
// an item from the collection in a way specific to the type of that item
interface Visitor {
    void visit(Circle c);
    void visit(Square s);
}

class Collection {
    Map<String, Shape> shapes = new HashMap<>();

    void add(String key, Shape shape) {
        shapes.put(key, shape);
    }


    // when you want to process what's behind a key, send in a visitor
    void visit(String key, Visitor visitor) {
        // ask the shape to be visited by the visitor
        shapes.get(key).visit(visitor);
    }
 }


 interface Shape { 
     void visit(Visitor visitor);
 }


 class Circle implements Shape {
     void visit(Visitor visitor) {
         // tells the visitor to treat this object as a circle
         visitor.visit(this);
     }
 }

假设您想要从集合中提取特定形状的东西。

 class DrawingVisitor implements Visitor {
     void visit(Circle c) {
         // use properties only a circle has to draw it
         graphics2d.ellipse(c.getRadius(), c.getCenterPoint()); 
     }

     void visit(Square s) {
         graphics2d.rectangle(s.getTopLeft(), s.getBottomRight());
     }
 }

等等

有道理吗?