Reputation: 1000
I am porting some software and it's working in Swift and JavaScript, but I cannot figure out how to do it in Java.
The JavaScript version looks like:
var obj = { "A": {
"1": (params) => { console.log("A1", params); },
"2": (params) => { console.log("A2", params); }
}, // A
"B": {
"1": (params) => { console.log("B1", params); },
"2": (params) => { console.log("B2", params);}
}, //B
}; // obj
Where at runtime, the application engine calls
var a = 'A';
var i = '2';
obj[a][i](params);
I for the life of me cannot figure out how to do this in Java. One key constraint (no pun intended) is that the code must be structurally similar to the other ports.
Previously, I was trying to use Object[][], with
Map<String, Object> map = Stream.of(obj).collect(Collectors.toMap(data -> (String) data[0], data -> (Object) data[1]));
to be able to embed some Map<String, Integer> objects. Example:
Map<String, Integer> colorMap = Stream.of(new Object[][] {
{"black", 0x000000},
{"navy", 0x000080},
};
I tried to arrive at similar code, but Java did not like it, because lambdas and Object are not compatible.
The code I am looking to arrive at is:
Map<String, Map<String, Callable<Void> > > obj = Stream.of(new Object[][] {
{"A", Stream.of(new Object[][] {
{"1", (params)->{ System.out.println("A1"); } },
{"2", (params)->{ System.out.println("A2"); } } }),
{"B", Stream.of(new Object[][] {
{"1", (params)->{ System.out.println("B1"); } },
{"2", (params)->{ System.out.println("B2"); } } } )}
}
});
...
// call the function (assuming A and 1 exist)
obj.get("A").get("1")(params);
But I am stumped on how to convert this to being able to use lambdas. The error I keep getting is:
error: incompatible types: Object is not a functional interface
or error: lambda expression not expected here
Upvotes: 2
Views: 263
Reputation: 111239
One possible structure-preserving translation to Java is:
Map<String, Map<String, Runnable>> obj = Map.of(
"A", Map.of(
"1", () -> System.out.println("A1"),
"2", () -> System.out.println("A2")
),
"B", Map.of(
"1", () -> System.out.println("B1"),
"2", () -> System.out.println("B2")
)
);
You use it like this:
obj.get("A").get("2").run();
If you need to pass arguments, or return values from the functions, replace Runnable
with a different functional interface. You may have to defined one of your own. For example:
@FunctionalInterface
interface FunctionWithObjectParameters {
void run(Object... args);
}
Update: If you need to do this in Java 8, one option is to use an external library that has methods to construct maps in a more convenient way - like guava collect.
Another option is to implement the convenience methods yourself. Map.of
does not do any magic, you can easily replace it with your own "mapOf" method:
static <K, V> Map<K, V> mapOf(K key1, V value1) {
return mapOfEntries(entry(key1, value1));
}
static <K, V> Map<K, V> mapOf(K key1, V value1, K key2, V value2) {
return mapOfEntries(entry(key1, value1), entry(key2, value2));
}
static <K, V> Map<K, V> mapOf(K key1, V value1, K key2, V value2, K key3, V value3) {
return mapOfEntries(entry(key1, value1), entry(key2, value2), entry(key3, value3));
}
// Add more overloads if you need
static <K, V> Map<K, V> mapOfEntries(Map.Entry<K, V>... args) {
// On Android, you may want to use ArrayMap instead of HashMap
Map<K, V> map = new HashMap<>();
for (Map.Entry<K, V> arg : args) {
map.put(arg.getKey(), arg.getValue());
}
return map;
}
static <K, V> Map.Entry<K, V> entry(K key, V value) {
return new AbstractMap.SimpleEntry<>(key, value);
}
Upvotes: 6