Subclassing Array
The Array
class can be subclassed, using some lesser-known Java generics behavior.
First, a bit of clarification: if you are subclassing, you most likely want to refer to BaseArray
,
which is the actual class in which the Array
behavior is defined.
The common Array
class subclasses itself from BaseArray
using a self-referential generic (this
does get a bit complicated):
public class Array<T extends Object> extends BaseArray<T, Array<T>>
And BaseArray
:
public abstract class BaseArray<T extends Object, C extends BaseArray<T, C>>
extends ArrayList<T>
implements Sequence<T>
What all of that means is that BaseArray
accepts a collection (of type C
) that contains objects
of type T
. And BaseArray
extends ArrayList
, with its elements being of type T
.
Where C
is relevant is that BaseArray
has methods that return the same type of object as
BaseArray
, even if subclassed.
For example, the unique
(as well as others, including append
, elements
, sorted
, compact
,
get(from, to)
) method returns an instance of type C
:
public C unique() {
C uniqueList = newInstance();
for (T obj : this) {
if (!uniqueList.contains(obj)) {
uniqueList.append(obj);
}
}
return uniqueList;
}
The subclass of BaseArray
, such as Array
, is expected to define a method newInstance
that
returns an object of that type, which in Array
is:
public Array<T> newInstance() {
return new Array<T>();
}
Similarly, IntegerList
has:
public IntegerList newInstance() {
return new IntegerList();
}
And is subclassed from BaseArray
as:
public class IntegerList extends BaseArray<Integer, IntegerList>
Again, that’s telling BaseArray
that the elements (T
) are of type Integer
, and the collection
itself is an IntegerList
. That makes the following possible:
IntegerList ilist = IntegerList.of(2, 3, 2, 3, 3, 5, 5, 3)
IntegerList result = ilist.unique();
assertEqual(IntegerList.of(2, 3, 5), result, message("ilist", ilist));
That is one of the more powerful parts of IJDK, that subclasses retain their own references. In
comparison, the JDK java.util.ArrayList#subList
method returns a java.util.List
, making it less
valuable for anyone subclassing ArrayList
and wanting subList
to return a list of the same type
as the subclass.
Thus, one might want to use the get(from, to)
method instead of subList
:
IntegerList ilist = IntegerList.of(2, 3, 1, 7, 8));
IntegerList result = ilist.get(1, -2);
assertEqual(IntegerList.of(3, 1, 7), result, message("ilist", ilist));
This is complicated, but gives BaseArray
subclasses flexibility that does not exist in the
equivalent JDK classes. The IJDK Str
class is undergoing similar revisions, returning references
to Str
instead of java.lang.String
, which it wraps.