Kotlin: как обрабатывать функцию любого типа ввода и заданного типа вывода

У меня есть функции с сигнатурой (ShellInput) -> ShellOutput . Ссылка на них хранится на карте:

 mutableMapOf<String, (ShellInput) -> ShellOutput>("trim" to ::trim) 

Затем они извлекаются из этой карты и вызывается через отражение. Теперь необходимо иметь функции, в которых тип ввода может меняться, поэтому я попытался:

 mutableMapOf<String, (Any) -> ShellOutput>("trim" to ::trim) 

Но это не работает. Как я могу справиться с этим?

Фактически, при установке ::trim в качестве значения в mutableMapOf<String, (Any) -> ShellOutput> будет mutableMapOf<String, (Any) -> ShellOutput> безопасность типов: когда вы берете значение из карты после этого, оно набирается как (Any) -> ShellOutput , позволяя вам передать Any как аргумент функции. Система типов не позволяет этого.

В качестве обходного пути вы можете использовать звездообразный тип Function1<*, ShellOutput> , что означает, что тип аргумента неизвестен:

 mutableMapOf<String, Function1<*, ShellOutput>>("trim" to ::trim) 

Когда вы получите значение с этой карты, вы увидите, что тип аргумента, который принимает функция, – Nothing . Это полностью ожидаемо, система типов снова сохраняет безопасность типов: нет ничего безопасного перехода к функции с неизвестным типом параметра ( Nothing является типом без значений).

Теперь пришло время показать компилятору, что вы знаете больше о типах, чем он, используя непроверенный актерский состав:

 @Suppress("UNCHECKED_CAST") val trim = functions["trim"] as Function1<ShellInput, ShellOutput> 

Вы можете инкапсулировать бросок в расширение, например:

 @Suppress("UNCHECKED_CAST") fun <T> Map<String, Function1<*, ShellOutput>>.getWithParameter(key: String): Function1<T, ShellOutput> = get(key) as Function1<T, ShellOutput> // Usage: functions.getWithParameter<ShellInput>("trim") 

В качестве альтернативы используйте KFunction<ShellOutput> . Это избавит вас от непроверенных бросков, но с самого начала он менее безопасен, так как он не контролирует количество аргументов, вы можете .call(...) ссылаться на такие функции с любым количеством аргументов любого типа.