7

I get a ClassCastException error when I run this because of the implicit cast of d to a double when I run the code. However, if I change reference to d in to Object[] then it can't be a parameter to the set function. If I change the set function to accept an Object[] then everything works fine, but then the class will fail at runtime if anyone calls set on an object that isn't of type N.

I need a solution that can get() the old array (or a clone thereof) and that can set() data to a new array.

public class Foo<N> {

    public static void main(String[] args) {
        Foo<Double> foo = new Foo<Double>();
        Double[] d = foo.get();

        // do stuff to d ...

        foo.set(d);
    }

    N[] data;

    public Foo() {
        data = (N[]) new Object[2];
    }

    public N[] get() {
        return (N[]) data;
    }

    public void set(N[] data) {
        this.data = data;
    }

}
Lii
  • 9,906
  • 6
  • 53
  • 73
avisnacks
  • 81
  • 5

4 Answers4

1

There's a trick to this.

class Foo<N> {

    // Deliberately no parameters.
    N[] myArray = makeArray();

    private N[] makeArray(N... ns) {
        return ns;
    }

    public N[] get() {
        return myArray;
    }
}

This may look like there is no casting (which is a good thing) but actually there is, it is just being done by the varargs system.

OldCurmudgeon
  • 60,862
  • 15
  • 108
  • 197
  • Maybe I wasn't clear w the question but in my case the array is already instantiated and I need to make a change to it. So I need to get it from foo do something to it. And reset it. I can't have a solution that instantiates it in the constructer. – avisnacks Dec 24 '15 at 10:51
  • 2
    @avisnacks: In your example you create the array in the `Foo` constructor. Is that not how it should work? Can you update the question to make that clearer? – Lii Dec 24 '15 at 11:13
  • @OldCurmudgeon: This does not work. You will always get an `Object` array in `myArray`. When the initialiser calls `makeArray` there is no trace of `N` left, it has been erased. Generic vararg method always creates `Object` arrays. It is not possible to create arrays of a generic type at all. That is described for example in the [Java Tutorial](https://docs.oracle.com/javase/tutorial/java/generics/restrictions.html#createArrays). – Lii Dec 24 '15 at 11:25
  • @Lii - After erasure **all** arrays are `Object[]`. This *trick* merely avoids casting. – OldCurmudgeon Dec 24 '15 at 12:04
  • Okay, it seems like I misunderstood the purpose, sorry. *"After erasure all arrays are `Object[]`"* I'm not sure what you mean here. All arrays of a *generic type* will be `Object[]` at runtime. But not the other ones, `Double[]` and `double[]` will have those runtime types. Erasure is only concerned with generic types. – Lii Dec 25 '15 at 10:44
1

To create an array with the right runtime type requires some kind of runtime representation of that type. A generic class like Foo<N> has no runtime representation of N.

There are two solutions:

  1. Use a List<N> instead. This is best if it is possible!
  2. Manually add a runtime representation of N by passing in a Class<N> to Foo, use java.lang.reflect.Array.newInstance to create the array.

Code for the second solution:

public Foo(Class<N> newDataClass) {
    data = (N[]) Array.newInstance(newDataClass, 2);
}

Foo<Double> foo = new Foo<>(Double.class);

EDIT:

If what you want to do is instead to get a copy of an existing Double array you can do that with (N[]) data.clone(). Setting it will not be a problem.

Lii
  • 9,906
  • 6
  • 53
  • 73
1

A convenient (but verbose) way to do this would be to change the constructor to take a dummy argument with the array type, since you know that it is double at your main method.

  public class Foo<N> {
         N[] data;

         public Foo(N inType) {
             data = (N[]) Array.newInstance(inType.getClass(), 2) ;
         }

         public N[] get() {
             return (N[]) data;
         }

         public void set(N[] data) {
             this.data = data;
         }

         public static void main(String[] args) {
             Foo<Double> foo = new Foo<Double>(new Double(0));
             Double[] d = foo.get();
             // do stuff to d ...
             foo.set(d);
         }

  }
yiannis
  • 1,411
  • 12
  • 21
0

The problem with a generic is, that it does not have any specific type at runtime. It's just the class Object, which means, that creating such object is completely pointless, because you would, in fact, just create an object of type Object.

You can however create such objects from outside of the class, because the real type of the object is known here (Double). Thus I would suggest to use some sort of List, which can be dynamic, to store the values.

qwertz
  • 13,850
  • 9
  • 31
  • 45