i'm in need of a data container for several data types and fail to write some omnipotent :-) converter func.

Maybe my idea is crab, in this case please give me a hint what concept to use.

My current template is as follows:

    public static Object convert(Class<?> dataType, String data) {
        switch (dataType) {
        case JsonNode.class:
            return new ObjectMapper().readTree(data);
            break;
        case Integer.class:
            return Integer.parse(data);
            break;
        default:
            break;
        }
    }

But it wont compile with errors:

Case constant of type Class<JsonNode> is incompatible with switch selector type Class<capture#1-of ?>

Case constant of type Class<Integer> is incompatible with switch selector type Class<capture#1-of ?>

Am I completely mislead in my solution or is there a valid syntax to get this done?

4 Replies 4

You're going to have to use a chain of ifs. Sorry if you were hoping for something better.

You can't use those in switches. Switches aren't quite 'a replacement for a bunch of if / else if statements'.

More generally I wouldn't do it this way at all. For many reasons:

  • There's an alternative. I think it's pretty good - see below.
  • You can't generate a compile-time error if someone calls convert with a hardcoded data type that you don't support. For example, if you don't have a rule for converting to, say, Boolean, and I write convert(Boolean.class, "true"), it'd be nice if I get an error as I write that line instead of having to wait for the runtime to tell me.
  • If you want to convert to a genericsed type, such as: "Please convert this to a List<Integer>", you can't; Class<?> cannot represent generics. List.class works, List<Integer>.class does not. (There's a hack called 'super type tokens' that sort of can but it's convoluted, wonky syntax, and will confuse readers. I wouldn't go for those lightly!)
  • The code is a bit of a mess as it'll have to be a giant if/elseif block.
  • If that list grows long, the code has to walk through every case every time, i.e. it's not very performant.

An alternative:

public final class DataConverters {
  private DataConverters() {} // No instantiation

  public static final Function<String, Integer> TO_INTEGER = v -> Integer.parse(v);
  public static final Function<String, JsonNode> TO_JSON = v -> new ObjectMapper().readTree(v);
  // and so on
}

// to use:

String v = readSomeNumber();
int n = DataConverters.TO_INTEGER.apply(v);

If you also need dynamic behaviour, i.e. where some code doesn't know what type is needed and instead the caller to this code tells it what is needed:

public <T> T readFromDb(Db db, String unid, Function<String, T> converter) throws SQLException {
  var s = db.select("SELECT data FROM dataTable WHERE unid = ?", unid).singleString();
  return converter.apply(s);
}

The above would read data from a database table, and convert it. A caller could for example write: int v = readFromDb(db, "12345", DataConverters.TO_INTEGER);.

Note that you get type safety (these methods return the type you're converting to, not 'Object'), and you can't convert to a type you have no converter for.

If you MUST make it even more dynamic than that, which is a giant code smell and can only be excused if you're interacting with a language with less strict typing rules, you could even do something like this:

// in DataConverters

private static final Map<Class<?>, Function<String, ?>> ALL_CONVERTERS; static {
  var m = new HashMap<Class<?>, Function<String, ?>>();
  m.put(Integer.class, TO_INTEGER);
  // one 'put' call for every converter you wrote.
  ALL_CONVERTERS = Collections.unmodifiableMap(m);
}

public static <T> T convert(Class<T> type, String data) {
  return type.cast(ALL_CONVERTERS.get(type).apply(data));
}

With that last trick you lose the ability to have converters that convert to genericsed types (you can't have Function<String, List<String>> TO_STRING_LIST = ...;).

If there are no class name conflicts, you can use:

switch( dataType.getSimpleName() ) {
   case “String” -> your_code; 
}

and if there are: *

switch( dataType.getCanonicalName() ) { 
   case “java.lang.String” -> your_code; 
}

@MarcoPuente that's pretty bad. A high 'hey it works, dont care about maintainability' vibe. You could easily write a typo and you won't get a warning. There's a good reason "Stringly typed" is an insult. Please don't write that.

Your Reply

By clicking “Post Your Reply”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.