【问题标题】:Why can't I initialize an array of custom class为什么我不能初始化自定义类的数组
【发布时间】:2016-08-19 23:02:00
【问题描述】:

我正在尝试在 Java 中实现一个基本的哈希图,但我一直在纠结为什么我不能声明我的自定义类 KVPair 的数组。在多次尝试修复我的构造函数中的数组声明后,我得到一个错误:

contains = new KVPair[capacity];

当我尝试这个时,我收到一个编译错误,提示我“无法创建 HashMap.KVPair 的通用数组。”

我还从另一个 stackexchange 答案中看到,该答案建议将对象数组转换为类似这样的其他东西:

contains = (KVPair[])new Object[capacity];

执行此操作时,我收到运行时错误消息“java.lang.ClassCastException: [Ljava.lang.Object; 无法转换为 [MyHashMap$KVPair;。”

下面我包含了我的 hashmap 类的构造函数之一以及我的 KVPair 类。任何有关如何解决此问题的帮助将不胜感激。

public class MyHashMap<K, V> implements Iterable<K> {

private static final int DEFAULT_CAPACITY = 200;
private static final double DEFAULT_LOAD_FACTOR = 0.7;

private int capacity; // the number of buckets in the map
private int size; // the number of items that have been put into the map
private double loadFactor;

KVPair[] contains;

// Constructs an empty map.
public MyHashMap() {
    capacity = DEFAULT_CAPACITY;
    this.loadFactor = DEFAULT_LOAD_FACTOR;
    contains = (KVPair[]) new Object[capacity];
}

... 

public class KVPair {
    private K key;
    private V value;
    private KVPair next;
    private int hash;
    private KVPair(Object k, Object v){
        key = (K) k;
        value = (V) v;
        next = null;
        hash = k.hashCode();
    }
    public KVPair(Object k, Object v, KVPair nextKV){
        key = (K) k;
        value = (V) v;
        next = nextKV;
        hash = k.hashCode();
    }

}

【问题讨论】:

  • 好吧,你的语法错误,你使用的是原始类型进行转换。然而,仅凭这一点并不能解决问题。 Java 的泛型是不可重构的,并且运行时实际上无法验证转换。你必须使用@SupressWarnings:stackoverflow.com/questions/1129795/…
  • @markspace 在编译时没有任何东西会导致强制转换异常。这与语法无关
  • @markspace 你是什么意思我的语法错误?我还应该如何将我的 Object 数组转换为 KVPair 数组?
  • 你为什么将kv作为KVPair的构造函数传递给Objects,然后再进行转换?为了类型安全,为什么不首先将它们设为 KV

标签: java arrays generics classcastexception


【解决方案1】:

通常需要底层泛型数组的集合实现通过使用Object[] 类型的非泛型数组来解决此问题。只要数组作为实现细节被隐藏并且集合公开的方法都是泛型的,那么这样做是完全类型安全的。您可以在 JDK 代码中特别看到这一点(例如:http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/8u40-b25/java/util/ArrayList.java#ArrayList)。

另一个实例化泛型数组的解决方案是使用Array.newInstance。但是,这需要传递泛型类型的类,您很可能不希望将其强加给 API 的用户。只是为了记录,这里可以用来创建一个通用的数组工厂方法:

public class ArrayUtils {
    @SuppressWarnings("unchecked")
    public static <T> T[] ofDim(Class<T> clazz, int length, T defaultValue) {
        T[] arr = (T[]) Array.newInstance(clazz, length);
        for (int i = 0; i < length; i++) arr[i] = defaultValue;
        return arr;
    }
}

简而言之:

  • 如果您的通用数组可以作为实现细节隐藏,请使用裸Object[]
  • 如果您的泛型数组将被外部类传递并可能发生变异,您需要使用Array.newInstance 确保运行时类型安全

【讨论】:

  • 感谢您的回答。在您使用裸 Object[] 的第一个建议中,您的意思是我应该将其类型转换为 KVPair[],还是应该将其保留为对象数组?如果我将它保留为对象数组,我将找不到使用迭代器遍历键的方法,因为我无法引用 KVPair 的键,因为它被视为对象。
  • 我向您指出了 JDK 中 ArrayList 的实现。他们使用普通的Object[] 并在他们读回元素时简单地转换元素。正如我所说,如果您的数组从外部不可见,这只是一种有效的方法
猜你喜欢
  • 2012-01-07
  • 2023-03-28
  • 2023-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-02-09
  • 2013-03-13
  • 1970-01-01
相关资源
最近更新 更多