Random Item Chooser

import java.util.*;

/** The StringChooser interface requires classes that implement the interface to implement
 *  a method called getNext that is defined in terms of zero parameters and returns a value
 *  of type String.
 */
interface StringChooser {
    String getNext();
}

/** The PseudoRandomNumbers class and the classes it depends on define a program that
 *  outputs a series of values where each value is a fixed length string denoting an
 *  integer. The values in the series may contain leading zeros and are each followed by
 *  a delimiter value, which may be an empty string. If the array of String values passed
 *  to the program's main method is not null and the length of the array is greater than
 *  zero, then the delimiter is the value of the first String value in the array;
 *  otherwise, the delimiter is a String value comprised of a single space character.
 *
 *  Example 1
 *
 *  At 3 AM (PST) on 7-FEB-2019, the author used PseudoRandomNumbers to generate the
 *  following series of values.
 *
 *  853 927 068 972 146 503 149 431 675 802 802 068 853 503 149 972
 *
 *  Questions
 *
 *  1. In what ways are the value of the sequence shown in Example 1 not random?
 *  2. What simple change can you make to the main method so that the program produces
 *     sequences of length 16 that are more random than the one shown in Example 1?
 *  3. What command line argument(s), if any, were used to generate the following output?
 *
 *     6120078098105940923042307410070065803650423036505940741065800780
 */ 
public class PseudoRandomNumbers
{
    private static final int numCount = 16;
    private static final int numLength = 3;

    public static void main(String args[]) {
        final String delimiter = args != null && args.length > 0 ? args[0] : " ";
        final StringChooser chooser = new FixedLengthRandomIntegerChooser(numLength);
        for (int n = 1; n <= numCount; n++) {
            System.out.print(chooser.getNext() + delimiter);
        }
    }
}

/** The FixedLengthRandomIntegerChooser class implements the StringChooser interface: it
 *  defines a type of object that has a getNext method, which always returns a String
 *  value of fixed length denoting an integer. When a FixedLengthRandomIntegerChooser
 *  object is constructed, the length of the String values returned by getNext must be
 *  specified; optionally, the maximum number of String values in a pool of values that
 *  strings are randomly selected from may also be specified. When all of the strings
 *  in the pool have been consumed as a result of calling getNext, the pool of strings
 *  is replenished with the same set of strings that it contained when the
 *  FixedLengthRandomIntegerChooser object was constructed.
 */
final class FixedLengthRandomIntegerChooser implements StringChooser
{
    private static final int defaultNumberPoolSize = 10;
    private static final String digitPool = "0123456789";
    private final RandomStringChooser chooser;

    public FixedLengthRandomIntegerChooser(final int numberLength, final int numberPoolSize) {
        final String[] numberPool = getIntegersAsStrings(numberLength, numberPoolSize);
        chooser = new RandomStringChooser(numberPool);
    }
    public FixedLengthRandomIntegerChooser(final int numberLength) {
        this(numberLength, defaultNumberPoolSize);
    }
    public String getNext() {
        String next = chooser.getNext();
        if (next.equals("NONE")) {
            chooser.reset();
            next = chooser.getNext();
        }
        return next;
    }
    public static String[] getIntegersAsStrings(final int length, final int count) {
        final String[] numbers = new String[count];
        RandomDigitChooser digitChooser = new RandomDigitChooser(digitPool);
        for (int i = 0; i < count; i++) {
            String number = "";
            while (number.length() < length) {
                Integer digit = digitChooser.getNext();
                if (digit == null) {
                    digitChooser.reset();
                    digit = digitChooser.getNext();
                }
                number += digit;
            }
            numbers[i] = number;
        }
        return numbers;
    }
}

/** The ResettableItemChooser<T> interface requires classes that implement the interface
 *  to implement: a method called reset that returns no value (i.e. has a void return type)
 *  and is defined in terms of zero parameters; a method called getNext that returns a value
 *  of type T and is defined in terms of zero parameters.
 */
interface ResettableItemChooser<T> {
    void reset();
    T getNext();
}

/** The RandomItemChooser<T> class implements the ResettableItemChooser<T> interface:
 *  it defines a generic type of object that maintains a set of available items of type T,
 *  any one of which may be chosen at random and removed from the set using the object's
 *  getNext method, which is defined in terms of zero parameters. A RandomItemChooser<T>
 *  object is constructed from either a non-null array of items of type T or from a non-null
 *  array of items of type T and a value of type T (passed to the constructor in that order),
 *  the latter of which is the value returned by getNext when the set of items maintained by
 *  the RandomItemChooser<T> object is empty; if not otherwise specified, the value null
 *  is returned by getNext when the set is empty. An object of type RandomItemChooser<T>
 *  has a method called reset (defined in terms of zero parameters) that, when called,
 *  removes all items from the set of available items and then adds all of the items that
 *  were originally included in the set when the object was first created, assuming the array
 *  of items that was passed to the constructor function was not subsequently modified.
 */
class RandomItemChooser<T> implements ResettableItemChooser<T>
{
    private final T none;
    private final T[] items;
    private final List<T> list;
    
    public RandomItemChooser(final T[] items, final T none) {
        this.items = items;
        this.none = none;
        list = new ArrayList<T>(items.length);
        reset();
    }
    public RandomItemChooser(final T[] items) {
        this(items, null);
    }
    final public void reset() {
        list.clear();
        for (T item : items) {
            list.add(item);
        }
    }
    public T getNext() {
        final int n = list.size();
        final int i = (int)(Math.random() * n);
        return n > 0 ? list.remove(i) : none;
    }
}

/** The RandomStringChooser class extends RandomItemChooser<T>. A RandomStringChooser
 *  object is constructed from an array of String values. When the set of String values
 *  maintained by a RandomStringChooser object is empty, the value returned by invoking
 *  the object's getNext method is the String value "NONE".
 */
class RandomStringChooser extends RandomItemChooser<String>
{
    public RandomStringChooser(final String[] strings) {
        super(strings, "NONE");
    }
}

/** The RandomIntegerChooser class extends RandomItemChooser<T>. A RandomIntegerChooser
 *  object is constructed from a non-null array of Integer values. When the set of Integer
 *  values maintained by a RandomIntegerChooser object is empty, the value returned by
 *  invoking the object's getNext method is the value null.
 */
class RandomIntegerChooser extends RandomItemChooser<Integer>
{
    public RandomIntegerChooser(final Integer[] integers) {
        super(integers);
    }
}

/** The RandomDigitChooser class extends RandomIntegerChooser. A RandomDigitChooser object
 *  is constructed from a non-null String value composed of one or more characters in the
 *  set {'0', '1', '2', ...., '9'}. The class has a static method called getDigits that
 *  takes a String of digit characters as input and returns a corresponding array of
 *  Integer values.
 */
final class RandomDigitChooser extends RandomIntegerChooser
{
    public RandomDigitChooser(final String digits) {
        super(getDigits(digits));
    }
    public static Integer[] getDigits(final String digits) {
        final int n = digits.length();
        final Integer[] array = new Integer[n];
        for (int i = 0; i < n; i++) {
            final String digit = digits.substring(i, i + 1);
            array[i] = Integer.parseInt(digit);
        }
        return array;
    }
}