【问题标题】:Cannot instantiate generic data type in class无法在类中实例化泛型数据类型
【发布时间】:2017-09-27 17:25:34
【问题描述】:

我有一个不可变的类,具有以下布局,

public final class A <T> {
    private ArrayList<T> myList;
    private A(){
        myList = new ArrayList<T>();
    }
    public A<T> addData(T t){
        A newOb = // make a new list and instantiate using overloaded constructor
        T newT = new T(); ***********ERROR HERE*************
        newOb.myList.add(newT);
        return newOb;
    }
    .........
}

我在这里得到的错误是 cannot instantiate type T 。现在,我认为这可能与 Java 中的 type erasure 有关。

我该如何克服这个问题?我想将传递给 addData 的参数的新副本添加到我的列表中。

【问题讨论】:

  • 对象是在运行时创建的。所以我们在运行时需要适当的字节码。最大 Java 擦除可以将 T newT = new T(); 转换为 Object newT = new Object() 以在运行时可用。但这也是错误的,因为T 可以是任何东西extends Object 不仅仅是Object。因此编译器错误。

标签: java generics


【解决方案1】:
T newT = (T) t.getClass().newInstance() // assuming zero args constructor and you'll
                                        // have to catch some reflection exceptions

【讨论】:

  • 我使用了这个,T 在我的类中是整数,它在运行时抛出异常。
  • @sTEAK.: 所以如果你能做到new T(),那么当TInteger 到底意味着什么?
  • 在编译时 T 被 Object 替换
【解决方案2】:

在 Java 8 中,您可以传递一个 factory lambda,它会创建所需类型的新实例:

public final class A <T> {
    private ArrayList<T> myList;
    private A(){
        myList = new ArrayList<T>();
    }
    public A<T> addData(Supplier<T> factory){
        A newOb = // make a new list and instantiate using overloaded constructor
        T newT = factory.get();
        newOb.myList.add(newT);
        return newOb;
    }
    .........
}

例如这样使用它:

A<Integer> a = new A<>();
a.addData( () -> new Integer(0) );

内置的无参数Supplier接口可以作为回调的封装。

【讨论】:

  • 这应该是要走的路,没有变通办法和/或假设......
  • 应该是公认的答案。我不是 Java 专业人士,但我想显式类型转换很少是一个好兆头......专业人士怎么说?
  • 您好,很好的解决方案!我提出了一个疑问,希望您能分享一些启示,为什么我需要创建一个新的 A 只是因为它被声明为 final ?还是创建一个新类是最佳做法?
【解决方案3】:

在java语言中泛型是通过擦除实现的,所以不可能实例化一个泛型类型。也不可能创建一个泛型类型的数组等等。

我该如何克服这个问题?我想添加参数的新副本 正在传递给 addData 到我的列表中。

您可以尝试使用Cloneable 接口作为类型绑定或添加自己的类似接口。

【讨论】:

  • Cloneable 因为类型绑定完全没用
  • 除非它被正确使用并且clone()被实现,我想。
  • 为了调用表达式的方法,表达式的静态类型必须有这个方法
  • 所以T extends Object &amp; Cloneable 会更适用。不错的补充,我没有注意到。
  • 一切都会自动扩展Object。那没关系。 Objectclone 不是公开的,仅用于子类调用超级实现。 Java 中没有指定公共clone 方法的抽象类型。
【解决方案4】:

我正在寻找类似问题的解决方案。这是我提出的解决方案:

public final class ImmutableA<T> {
private ArrayList<T> myList;
private Class<T> clazz;

public static <E> ImmutableA<E> createEmpty(Class<E> clazz) {
    return new ImmutableA<>(clazz);
}

private ImmutableA(Class<T> clazz) {
    this.myList = new ArrayList<T>();
    this.clazz = clazz;
}

public ImmutableA<T> addData(T t) {
    ImmutableA<T> newOb = new ImmutableA<>(clazz);
    try {
        Constructor<T> constructor = clazz.getDeclaredConstructor(clazz);
        T newT = constructor.newInstance(t);
        newOb.myList.add(newT);
        return newOb;
    } catch (NoSuchMethodException | InvocationTargetException | InstantiationException | IllegalAccessException e) {
        e.printStackTrace();
    }
    return null;
}

public ArrayList<T> getMyList() {
    return myList;
}

public static void main(String[] args) throws InstantiationException, IllegalAccessException {
    ImmutableA<String> immutableA = createEmpty(String.class);
    System.out.print(immutableA.getMyList().toString());
    immutableA = immutableA.addData("a");
    System.out.print(immutableA.getMyList().toString());
    immutableA = immutableA.addData("b");
    System.out.print(immutableA.getMyList().toString());
    immutableA = immutableA.addData("c");
    System.out.print(immutableA.getMyList().toString());
}

}

希望这对某人有所帮助。

【讨论】:

    【解决方案5】:

    你可以像这样使用 clone() 方法:

    public final class A <T extends Cloneable> {
        private ArrayList<T> myList;
        private A(){
            myList = new ArrayList<T>();
        }
        public A<T> addData(T t){
            T newT = t.clone();
            newOb.myList.add(newT);
            return newOb;
        }
        .........
    }
    

    【讨论】:

    • 假设我的 T 是 Date ,我是 Date d = new Date(); A a = new A(); a.addData(d); d.setDate(10); ,那么 List 中的值会不会改变?\
    • 是的,您调用添加到列表中的可变对象的设置器,因此您还可以更改列表中的值。
    • 好的。这是创建要插入的数据的新副本的主要目的。但事实证明它不起作用。
    • 如果您创建 myList.add(d.clone()) 然后调用 d.setX() 这不会将值更改为列表
    • @jolivier 这个主意不错。但我认为克隆方法不适用于泛型。
    【解决方案6】:

    你可以得到T的类型

    Type type = new TypeToken<T>(){}.getType();
    

    然后得到一个 T 的实例

    type.getClass().newInstance();
    

    完整示例

    public T foo() {
        try {
            return (T) type.getClass().newInstance();
        } catch (Exception e) {
            return null;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-10-03
      • 2011-04-02
      • 2013-09-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多