Search code examples
javagenericsmethod-parameters

Read list of type parameter values using Scanner.nextX that matches the type parameter


There are so many posts related to this one that I don't think it would be helpful to reference them, but if people find particularly useful ones it would be great to have them added here.

  ArrayList<Integer> readIntegersFrom(Scanner scnr) {
      ArrayList<Integer> lst = new ArrayList<Integer>();
      while (scnr.hasNext()) {
         lst.add(scnr.nextInt());
      }
      return lst;
   }

Integer appears in 4 places:

  1. the name of the method
  2. the Type parameter of the ArrayList return type
  3. the Type parameter of lst's type
  4. the Type parameter where a new list is created

So far so good. However, my program duplicates this method four times (with, I'm sure, more to come), each with a different type parameter and Scanner method. (For example, ArrayList<Double> and scnr.nextDouble().)

I don't doubt that I am confounding ideas and techniques from the many other languages I have used, but is there any way to generalize this method so I can tell it I want an ArrayList of, say, Double, and it should use Scanner.nextDouble()? The two parts of this problem are (a) communicate the type parameter of the ArrayList and (b) communicate the Scanner method to use.

I would settle for a single method where the type parameter is specified or deduced and have an enum argument to tell the scanner which method to use, though the two are directly connected.


Solution

  • You'd need to provide additional parameters, for example:

      <T> ArrayList<T> readFrom(BooleanSupplier hasNext, Supplier<T> supplier) {
          ArrayList<T> lst = new ArrayList<>();
          while (hasNext.getAsBoolean()) {
             lst.add(supplier.get());
          }
          return lst;
       }
    

    and then invoke like:

    ArrayList<Integer> intList = readFrom(scnr::hasNextInt, scnr::nextInt);
    ArrayList<Double> doubleList = readFrom(scnr::hasNextDouble, scnr::nextDouble);
    ArrayList<String> stringList = readFrom(scnr::hasNext, scnr::next);
    

    If you don't fancy having that at every call site, you can define simple helper methods:

    ArrayList<Integer> readIntegersFrom(Scanner scnr) {
      return readFrom(scnr::hasNextInt, scnr::nextInt);
    }
    
    ArrayList<Double> readDoublesFrom(Scanner scnr) {
      return readFrom(scnr::hasNextDouble, scnr::nextDouble);
    }
    
    // etc
    

    and invoke like:

    ArrayList<Integer> intList = readIntegersFrom(scnr);
    ArrayList<Double> doubleList = readDoublesFrom(scnr);