Is it possible to have an interface for Java arrays?

655 Views Asked by At

Let's say we want to have a method in an interface that returns an array, like this:

interface A {
    B[] findAllB();
}

But arrays are very low-level and implemented definitively. Their implementation is final and cannot change, much like a final class. It is not possible to return anything else other than an array if the return type in this interface is already an array. So when it is better to avoid having arrays as return types because it restricts the freedom of the implementing class to return whatever it wants to return. So, we decide to use java.util.List:

interface A {
    List<B> findAllB();
}

But when implementing this interface, it might be extremely convenient for us to return an actual array, that in theory, does implement the List interface (e.g. with add and remove throwing UnsupportedOperationExceptions):

class AImpl implements A {
    List<B> findAllB() {
        B[] array = ...;
        ...
        return array; // does not work
    }
}

But this does not work because you cannot return a B[] instead of List<B>.

I was wondering if there is an interface, (or if it even is possible to have an interface) that the low-level Java array implements, or, in other words, it is safe to return an array in its stead.

If this were possible, java.util.List could also extend it so we can return arrays or lists interchangeably behind the curtains.

I already suspect this is impossible, but in the world of computers anything is possible, so who knows.

5

There are 5 best solutions below

7
passer-by On

Replace the last line (the one that "does not work") of

class AImpl implements A {
    List<B> findAllB() {
        B[] array = ...;
        ...
        return array; // does not work
    }
} 

by

 return Arrays.asList(array);

This is the simplest way to turn the array that you have into the List that you want. The list is directly backed by the array (and is fixed size, no adds or removes).

Documentation link

3
WJS On

You could do it like this. Specify the array as the type parameter when implementing the interface.

interface A<T> {
    T findAllB();
}

class Tryit implements A<int[]> {
    int[] test  = {1,2,3,4}; 
    public int[] findAllB() {
         return test;
    }    
}

System.out.println(Arrays.toString(t.findAllB()));

prints

[1, 2, 3, 4]
0
SMMH On

Sadly, as @Turing85 pointed out, arrays are "covariant", a feature that is deemed a mistake today:

Clearly this was considered a worthy compromise at the time. Contrast that with today: many regard array covariance as a mistake, in retrospect.

This basically means assigning objects of the wrong type to them generates a runtime error rather than a compile-time error, which is undesirable. Evidently Java's arrays are not as interchangeable with Lists as I had assumed.

The silver-lining however, is that even though this "design mistake" is probably not remediable given Java's prevalence, it has been fixed in Kotlin which is basically Java without all the old design flaws.

Arrays in Kotlin are invariant. This means that Kotlin does not let us assign an Array<String> to an Array<Any>, which prevents a possible runtime failure

So I guess my only choices are to either wait for Java designers to remedy this (unlikely), adopt Kotlin (takes time), or use List<T> with Arrays.asList for the time being.

PS: None of these options solve my original problem which was to have an interface that generalizes both lists and arrays, such as Sequential:

package java.util;

interface Sequential<T> {
    int getLength();
    T getAt(int index);
}

interface List<T> extends Collection<T>, Sequential<T> {
    ...
}

And allowing arrays to be implicitly casted to Sequential.

This is not that crazy, Java language designers could easily do this without breaking backward compatibility. They could even make Sequential<T> extend Iterable<T>. If only...

2
william0754 On

I don't have enough reputation to comment, so post it here. I agree with @WJS and you can use java.lang.reflect.Array class to access T, such as use t.getClass().isArray() to determine t is an array or not, or have default method to get T's length like this: default int getLen(T t){ return Array.getLength(t); }, and you also can get element from t using Array.get(Object array, int index) method by index.

2
Alexey Veleshko On

No, there is no such interface. List is the interface that abstracts the idea of an array-like datastructure.

You can freely convert between Lists and arrays via List.toArray, List.of and the ArrayList constructor.