I wrote a method which "typifies" a String
, and tries to infer the type of data held within it. (A slightly modified version of this gist). The method returns the inferred Class
and the original String
(possibly slightly modified -- surrounding whitespace trimmed, etc.) in a Map.Entry<Class, String>
. For instance, typify("3f")
returns <Float, "3.0">
, typify(" c ")
returns <Character, "c">
and so on.
My next step was to write a second method which "decodes" these returned Map.Entry
objects so they can be directly assigned to an object of the inferred type. For instance:
Float f = decodeTypify(typify("3.14f"))
Boolean b = decodeTypify(typify("false"))
...and so on. This code is below:
@SuppressWarnings("unchecked")
public static <T> T decodeTypify (Entry<Class, String> entry) {
// String
if (entry.getKey() == String.class)
return (T) entry.getValue();
// Boolean
else if (entry.getKey() == Boolean.class)
return (T) (Boolean) Boolean.parseBoolean(entry.getValue());
// Byte
else if (entry.getKey() == Byte.class)
return (T) (Byte) Byte.parseByte(entry.getValue());
// Character
else if (entry.getKey() == Character.class)
return (T) (Character) entry.getValue().charAt(0);
// Short
else if (entry.getKey() == Short.class)
return (T) (Short) Short.parseShort(entry.getValue());
// Integer
else if (entry.getKey() == Integer.class)
return (T) (Integer) Integer.parseInt(entry.getValue());
// Long
else if (entry.getKey() == Long.class)
return (T) (Long) Long.parseLong(entry.getValue());
// Float
else if (entry.getKey() == Float.class)
return (T) (Float) Float.parseFloat(entry.getValue());
// Double
else if (entry.getKey() == Double.class)
return (T) (Double) Double.parseDouble(entry.getValue());
// LocalDateTime
else if (entry.getKey() == LocalDateTime.class)
return (T) (LocalDateTime) stringAsDate(entry.getValue());
else return null;
}
This seems to work great, especially when combined with Java's new local variable type inference:
var f = decodeTypify(typify("literally anything"))
Now I don't need to care about the returned type at all, because Java takes care of giving f
the correct type. But notice that if the entry
argument to decodeTypify()
has a key which doesn't match any of the options in the big if-else
tree, then decodeTypify()
returns null
. Here's this method running in the jshell with Java 11.0.1:
jshell> var x = decodeTypify(typify(null))
x ==> null
I assigned a null
value to a local, type-inferred variable! This isn't supposed to be possible. A side effect of this (it seems) is that I can actually tell x
to have any type at all, with no warnings:
jshell> Object x = decodeTypify(typify(null))
x ==> null
jshell> String x = decodeTypify(typify(null))
x ==> null
jshell> Byte x = decodeTypify(typify(null))
x ==> null
Note that this is not the case with non-null
returns:
jshell> var x = decodeTypify(typify("3"))
x ==> 3.0
jshell> Boolean x = decodeTypify(typify("3"))
| Exception java.lang.ClassCastException: class java.lang.Double cannot be cast to class java.lang.Boolean (java.lang.Double and java.lang.Boolean are in module java.base of loader 'bootstrap')
| at (#21:1)
Did I break something? If not, can someone explain what's going on here?