0

Given an array of strings representing values and another string representing a primitive type, I need to parse the values strings into an array of the give primitive type, e.g. "double" and ["1", "2", "3"] would become [1.0, 2.0, 3.0].

The first solution that comes to mind would look something like this:

String format = "int16";
String[] values = {"1", "2", "3"};
switch (format)
{
    case "int16":
        short[] short_values = new short[values.length];
        for (int i = 0; i < values.length; i++) short_values[i] = Short.parseShort(values[i]);
        foo(short_values);
        break;
}

While this works, I can't help but feel like there is a more elegant way of doing this. Is there some way in Java to store a reference to a static method and a reference to a primitive type so that you could so something like this:

Functor parser;
Type type;

switch (format)
{
    case "int16":
        parser = Short.parseShort;
        type = short;
        break;
}
List<type> value_list = new ArrayList<>();
for (String value : values) value_list.add(parser(value));
foo(value_list.toArray(new type[0]);

Assuming that's not possible, is there any other way that I might be able to improve upon the first solution?

Thanks

3 Answers3

2

@Couper example is pretty nice. You can also do something like this.

public class MainClass {

    public static void main(String[] args) {
        String format = "int16";
        String[] values = {"1", "2", "3"};
        ParserInterface pi = null;
        Class<?> classType = null;
        switch(format) {
        case "int16":
            pi = Short::parseShort;
            classType = Short.class;
            break;
        }
        for(String value : values) {
            System.out.println(classType.cast(pi.parse(value)));
        }
    }

}

@FunctionalInterface
interface ParserInterface {
    public Object parse(String data);
}

You can make use of functional interface to hold a method reference and set a class type to type case the parsed value. It worked for me! Please share your feedback and concerns.

  • I think this is a better solution because you can implement the parsing and doesn't rely on the class constructor. – Couper May 13 '20 at 18:36
  • This looks super close to what I need! Is there a way I can use the classType variable to create either an array or an arraylist? – Peter Bialek May 13 '20 at 18:57
  • You can't create an `array` or a `List` at runtime, but you can use the `List` like this: `List l = new ArrayList<>(); for (String value : values) { l.add((T) classType.cast(pi.parse(value))); }` – Couper May 13 '20 at 21:27
2

If you are working with Java 8 or higher, and if you don't mind the resulting array being "boxed" types instead of primitives (Integer, Double, etc.), then streams can be an elegant solution.

Here is a code example:

import java.util.Arrays;
import java.util.stream.Stream;

public class Streams {
    public static void main(String [] args) {
        String[] values = {"1", "2", "3"};
        String format = "double";

        Stream<String> stream = Arrays.stream(values);
        Object [] arr = null;

        switch (format) {
            case "int16":
                arr = stream.map(Short::parseShort).toArray();
                break;

            case "double":
                arr = stream.map(Double::parseDouble).toArray();
                break;
        }

        for (Object o : arr) System.out.println(o);
    }
}

If you want primitive arrays, you can use stream methods mapToInt and mapToDouble (with parameters such as Integer::intValue), but you will have to define a separate array for each case.

You can also use another stream to unbox the whole array later in the code, as described here: (Un)boxing primitive arrays in Java

Lev M.
  • 6,088
  • 1
  • 10
  • 23
1

You can do something like this:

private <T> T parse(String value, Class<T> type) {
    try {
        return type.getConstructor(String.class).newInstance(value);
    } catch (Exception e) {
        return null;
    }
}

This works as long as the that class that you need to cast to has a constructor with a single String parameter.

Couper
  • 414
  • 5
  • 13