1

So I'm working on an implementation of a Grid class for a small, personal utilities library. After much work, I'm tidying up the Grid class and adding some functionality. One of the key pieces of functionality that I wish to use with my Grid class is to be able to take a single, 2D array of any given type as an argument to the constructor.

This worked fine until I realized that I can't compile any code that attempts to pass in any array of primitives to the constructor. Since autoboxing doesn't happen on primitive arrays, this presents a design problem to me in the form of code reuse. The method for initializing the Grid is the same regardless of the type of array passed in, even if they are primitives, but it appears that I need to write a separate constructor for all the different types of primitives. Which leaves me with 9 different constructors assuming I just use my base constructor (and I had planned to have constructors with different arguments for things such as grid-wrapping options).

Am I right in assuming that there is no way to avoid all this code duplication?

Aargonian
  • 222
  • 3
  • 12
  • I'm confused on what you mean by `autoboxing doesn't happen on primitive arrays`; any array is an object, and can be used for a generic type. Mind showing your code? I'm not sure if I'm understanding you correctly – Vince Dec 31 '14 at 03:11
  • You could use the [builder pattern](http://stackoverflow.com/questions/328496/when-would-you-use-the-builder-pattern/1953567#1953567) to get away from the telescoping constructors. – Edward J Beckett Dec 31 '14 at 03:11
  • @Vince Emigh Autoboxing doesn't occur on an array of primitives in such that an array of type int[] could not be interpreted by the compiler as an array of type Object[]. Since int, double, char, boolean, etc. are all primitives, they don't extend Object. As an array, the values don't all get automatically converted to their Value Object counterparts, like Integer or Character, as they do when passing only a single primitive value. – Aargonian Dec 31 '14 at 03:13
  • But couldn't you do ` Constructor(T array)` then call `new Constructor(new int[5])`? – Vince Dec 31 '14 at 03:15
  • 1
    @Eddie B I hadn't thought of using the Builder pattern for the telescoping. Still learning all the design patterns myself. Thank you for the tip! – Aargonian Dec 31 '14 at 03:20
  • @VinceEmigh: Since `int[]` is an object, you *could* most certainly do that. It doesn't get you anything though, since it's coming through as an `Object`. You can't also do a dynamic cast to the array type since the compiler can't guarantee if it's an array that comes through. – Makoto Dec 31 '14 at 03:20
  • In your title you mention generics. Is `Grid` a generic class `Grid`? It sounds like it ought to be. If so, it wouldn't make sense to have a constructor taking an `int[][]`. I think you should have a generic class `Grid` with a constructor taking a `T[][]` (or a `List extends List extends T>>`) and just forget about primitive arrays completely. – Paul Boddington Dec 31 '14 at 03:49
  • Can you solve your problem by making Grid a generic class, using `Grid` such that T is your 2D array? I don't see any advantage in insisting that the type be specified in a constructor. – arcy Dec 31 '14 at 04:07
  • Look at the various `copyOf` and `fill` methods in `java.util.Arrays`. There are so many of those, for the same reason as you have so many constructors. – Dawood ibn Kareem Dec 31 '14 at 04:15
  • I can't see the Builder pattern helping a lot here. That applies more in the case of multiple parameters that can be present or absent in different combinations. That's not the situation here: rather the questioner wants a series of simple constructors using different primative types. – sprinter Dec 31 '14 at 04:15
  • @pbabcdefp The Grid class is a generic class Grid. The problem is that that generics don't work well with primitive arrays, since although the array itself is an object, the elements are not, and autoboxing on the elements does not occur at compile-time, so the code will not compile with a single generic constructor. Instead, I need separate constructors for the 8 primitive types and 1 for all other Object Reference types. I don't want to ignore primitive types, as I plan for this to be a general-purpose utility (just like ArrayList). – Aargonian Dec 31 '14 at 05:03
  • @pbabcdefp That's true, but I'm making this for my own convenience and perhaps others' at a later point, rather than basing it off what the current standard libraries do. And for me, being able to turn an already-existing 2D array of primitives into a Grid and back again happens to be a requirement for at least two of my projects, if not more. – Aargonian Dec 31 '14 at 05:09

1 Answers1

4

You can avoid (most of) the duplication by using Array class. But it's not pretty: your constructor parameter would have do be of type Object, and you'd have to just trust your caller to pass the actual array there, and not a Socket or a Properties.

For example, you could do your own boxing like this:

<T> public T[][] boxArray(Class<T> clazz, Object array) {
    int height = Array.getLength(array);
    int width = height == 0 ? 0 : Array.getLength(Array.get(array, 0));
    T[][] result = (T[][]) Array.newInstance(clazz, height, width);
    for(int i = 0; i < height; i ++) {
        Object a = Array.get(array, i);
        for(int j = 0; j < width; j++) {
            result[i][j] = (T) Array.get(a, j); 
        }
    }
    return result;
}
Dima
  • 39,570
  • 6
  • 44
  • 70
  • 1
    I had no idea `Array.get` existed. I wouldn't recommend the OP actually doing this (his class should clearly be generic), but +1 for the ingenuity of this. – Paul Boddington Dec 31 '14 at 05:02
  • That can work for now. Thank you! But once I release it to a more public audience (once I open-source my library), I'll have to switch back to multiple constructors. For an open project, I'll take code duplication of the lesser of two evils when it comes to type-safety. For personal projects, though, this can work. – Aargonian Dec 31 '14 at 05:05
  • You can make it *kinda* type safe, by having multiple public one-line constructors taking primitive arrays, that all end up calling the same private one, containing common logic that operates on `Array`. That's how I would do it anyway. (For the record, I strongly disagree, that code duplication is a "lesser evil" to type (un)safety). – Dima Dec 31 '14 at 14:01