Java稍后使用泛型来设置数组的原始类型

发布于 2024-12-28 12:08:26 字数 530 浏览 2 评论 0原文

我正在尝试用 Java 编写一些简单的数字代码,稍后可以在 float 和 double 之间进行选择。我的类的简化版本如下所示:

public class UniformGrid<T> {

    public T[] data;

    public UniformGrid(int arrayDim) {

        data = new T[arrayDim];

    }
}

这不起作用,我在尝试编译时遇到了通用数组创建错误。谷歌搜索并阅读一些 SO 答案,我了解了 java.lang.reflect.Array 并尝试使用

    data = (T[]) Array.newInstance(T.class, arrayDim);

Which 也不起作用,因为 T (可能)是原始类型。我的 Java 知识很生疏(尤其是在泛型方面),我想知道为什么 new 运算符不能与泛型数组类型一起使用。当然,我也对如何用 Java 解决这个问题感兴趣。

I am trying to write some simple numerical code in Java where one can choose between a float and double later. A simplified version of my class looks like the example below:

public class UniformGrid<T> {

    public T[] data;

    public UniformGrid(int arrayDim) {

        data = new T[arrayDim];

    }
}

This didn't work I got a generic array creation error when trying to compile. Googling and reading some SO answers I learned about java.lang.reflect.Array and tried to use

    data = (T[]) Array.newInstance(T.class, arrayDim);

Which also didn't work, since T is (probably) a primitive type. My Java knowledge is quite rusty (especially when it comes to generics) and I would like to know why the new operator cannot be used with a generic array type. Also of course I am interested in how one would solve this problem in Java.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

话少情深 2025-01-04 12:08:26

由于类型擦除,您无法在 Java 中创建泛型数组。解决这个问题的最简单方法是使用 List。但如果必须使用数组,则可以为数组使用 Object[] 并确保仅将 T 对象放入其中。 (这是 ArrayList 所采用的策略。)

例如:

private Object[] data = new Object[10];
private int size = 0;

public void add(T obj) {
    data[size++] = obj;
}

public T get(int i){
    return (T) data[i];
}

当然,您会从编译器收到未经检查的警告,但您可以抑制该警告。

You cannot create a generic array in Java because of type erasure. The easiest way to get around this would be to use a a List<T>. But if you must use an array, you can use an Object[] for your array and ensure that only T objects are put into it. (This is the strategy ArrayList takes.)

Ex:

private Object[] data = new Object[10];
private int size = 0;

public void add(T obj) {
    data[size++] = obj;
}

public T get(int i){
    return (T) data[i];
}

Of course you'll get an unchecked warning from your compiler, but you can suppress that.

听,心雨的声音 2025-01-04 12:08:26

创建数组时不能使用泛型,因为您在运行时不知道 T 是什么类型。这称为类型擦除

解决方案很简单:使用 List;数据

Generics can't be used when creating an array because you don't know at runtime what type T is. This is called type erasure.

The solution is simple: use List<T> data.

゛时过境迁 2025-01-04 12:08:26

抱歉,您必须采取另一种方法:

  1. 类型参数必须是引用类型,它们不能是原始类型。
  2. 只有引用类型支持多态性,并且仅适用于实例方法。原始类型则不然。 float 和 double 没有共同的超类型;您不能编写像 a + b 这样的表达式并在运行时选择是否执行浮点加法或双倍加法。由于 Java(与 C++ 或 C# 不同,它们为每个类型参数发出新代码)对泛型类型的所有实例使用相同的字节码,因此您需要多态性才能使用不同的运算符实现。

如果您确实需要这个,我会研究代码生成,也许作为自动构建的一部分。 (对源代码进行简单的搜索和替换应该能够将在双精度上运行的库转变为在浮点上运行的库。)

Sorry, you'll have to take another approach:

  1. Type parameters must be reference types, they can't be primitive types.
  2. Only reference types support polymorphism, and only for instance methods. Primitive types do not. float and double don't have a common supertype; you can not write an expression like a + b and choose at runtime whether to perform float addition or double addition. And since Java (unlike C++ or C#, which emit new code for each type parameter) uses the same bytecode for all instances of a generic type, you'd need polymorphism to use a different operator implementation.

If you really need this, I'd look into code generation, perhaps as part of an automated build. (A simple search & replace on the source ought to be able to turn a library operating on double into a library operating on float.)

绝影如岚 2025-01-04 12:08:26

这是可能的,只要您使用 FloatDouble 而不是 floatdouble,因为原始类型是Java 泛型中不允许。当然,这可能会很慢。而且,您将无法(安全地)允许公众直接访问该阵列。所以这个答案不是很有用,但理论上可能很有趣。无论如何,如何构造数组......

data = (T[]) new Object[arrayDim];

这会给你一个警告,但这并不是直接需要担心的事情。它以这种特定的形式工作 - 它位于通用构造函数内,并且 data 是对此新构造的对象的唯一引用。 查看此页面。

您不会能够以您喜欢的方式公开访问此数组对象。您需要在 UniformGrid 中设置方法来获取和设置对象。这样,编译器将确保类型安全,并且运行时不会给您带来任何问题。

private T[] data;
public void set(int pos, T t) {
        data[pos] = t;
}
public T get(int pos) {
        return data[pos];
}

在这种情况下,set 的接口将(在编译时)强制传递正确的类型。底层数组的类型是 Object[] ,但这没关系,因为它可以采用任何引用类型 - 并且所有泛型类型在运行时实际上都是 List或类似的类型反正。

有趣的是吸气剂。编译器“知道”data 的类型是 T[],因此 getter 将干净地编译并承诺返回 T。因此,只要您将 data 保持私有,并且仅通过 getset 访问它,那么一切都会好起来的。

一些示例代码位于 ideone 上。

public static void main(String[] args) {
        UniformGrid<A> uf = new UniformGrid<A>(1);
        //uf.insert(0, new Object()); // compile error
        uf.insert(0, new A());
        uf.insert(0, new B());
        Object o1= uf.get(0);
        A      o2= uf.get(0);
        // B      o2= uf.get(0); // compiler error
        System.out.println(o1);
        System.out.println(o2);
        System.out.println("OK so far");
        // A via_array1 = uf.data[0]; // Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [LA;
}

正如您所希望的, uf.insert(0, new Object())B o2= uf.get(0); 存在编译错误,

但您不应该' t 将 data 成员公开。如果这样做,您可以编写并编译A via_array1 = uf.data[0];。该行看起来应该没问题,但是您会遇到运行时异常:Ljava.lang.Object;无法转换为 [LA;

简而言之,getset 接口提供了一个安全的接口。但是,如果您在使用数组时遇到了这么多麻烦,那么您应该使用 ArrayList来代替。这个故事的寓意是:在任何语言(Java 或 C++)中,无论是否有泛型,都要对数组说不。 :-)

This is possible, as long as you use Float and Double instead of float and double, as primitive types are not allowed in Java Generics. Of course, this will probably be quite slow. And, you won't be able to (safely) allow direct public access to the array. So this answer is not very useful, but it might be theoretically interesting. Anyway, how to construct the array ...

data = (T[]) new Object[arrayDim];

This will give you a warning, but it's not directly anything to worry about. It works in this particular form - it's inside a generic constructor and data is the only reference to this newly constructed object. See this page about this.

You will not be able to access this array object publicly in the way you might like. You'll need to set up methods in UniformGrid<T> to get and set objects. This way, the compiler will ensure type-safety and the runtime won't give you any problems.

private T[] data;
public void set(int pos, T t) {
        data[pos] = t;
}
public T get(int pos) {
        return data[pos];
}

In this case, the interface to set will (at compile-time) enforce the correct type is passed. The underlying array is of type Object[] but that's OK as it can take any reference type - and all generic types are effectively List<Object> or something like that at runtime anyway.

The interesting bit is the getter. The compiler 'knows' that the type of data is T[] and hence the getter will compile cleanly and promises to return a T. So as long as you keep the data private and only access it through get and set then everything will be fine.

Some example code is on ideone.

public static void main(String[] args) {
        UniformGrid<A> uf = new UniformGrid<A>(1);
        //uf.insert(0, new Object()); // compile error
        uf.insert(0, new A());
        uf.insert(0, new B());
        Object o1= uf.get(0);
        A      o2= uf.get(0);
        // B      o2= uf.get(0); // compiler error
        System.out.println(o1);
        System.out.println(o2);
        System.out.println("OK so far");
        // A via_array1 = uf.data[0]; // Exception in thread "main" java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [LA;
}

As you would desire, there are compilation errors with uf.insert(0, new Object()) and B o2= uf.get(0);

But you shouldn't make the data member public. If you did, you could write and compile A via_array1 = uf.data[0];. That line looks like it should be OK, but you get a runtime exception: Ljava.lang.Object; cannot be cast to [LA;.

In short, the get and set interface provide a safe interface. But if you go to this much trouble to use an array, you should just use an ArrayList<T> instead. Moral of the story: in any language (Java or C++), with generics or without generics, just say no to arrays. :-)

山有枢 2025-01-04 12:08:26

Effective Java,第二版中的第 25 条讨论了这个问题:

数组是协变的且具体化的;泛型是不变的并且被删除。
因此,数组提供运行时类型安全,但不提供编译时类型安全,反之亦然。一般来说,数组和泛型不能很好地混合。

Item 25 in Effective Java, 2nd Edition talks about this problem:

Arrays are covariant and reified; generics are invariant and erased.
As a consequence, arrays provide run-time type safety but not compile-time type safety and vice versa for generics. Generally speaking arrays and generics don't mix well.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文