Проведение карты функций с различным количеством аргументов - PullRequest
0 голосов
/ 07 апреля 2020

Я пытаюсь написать служебный класс, который может принимать String и Class<?> и возвращать обратно Object, который действительно является типизированным экземпляром этого String.

Например, если у меня есть строка "FALSE" и класс "java.lang.Boolean", я хочу вернуть напечатанный Boolean.FALSE. Et c для других классов, таких как String, Long, Int, Enum и др. c.

Я хочу сделать это так, чтобы не требовалось слишком много переписывать, когда новый тип добавлено. В настоящее время у меня есть метод с подписью Object getTypedValue(Class<?> clazz, String value);.

Отсюда я думал, что смогу использовать Map<Class<?>, Function<String, Object>>. Это прекрасно работает для таких случаев, как String и Boolean, но в случае Enum мне также нужно имя класса. Итак, Карта становится Map<Class<?>, BiFunction<String, Class<?>, Object>>.

Но большинству методов не нужен параметр Class. Также обнаружили, что новый FunctionalInterface, такой как:

@FunctionalInterface
public interface OptionalClassFunction<T, CLAZZ, R> {

  R apply(T t, CLAZZ... clazz);

}

, кажется, не работает, когда я передаю метод с только T в качестве лямбды.

Например,

ImmutableMap.of(
  Boolean.class, Util::toBoolean
)

private Boolean toBoolean(String value);

не работает.

Любые предложения о том, как что-то вроде:

Map<Class<?>, WhateverFunction<String, MAYBE_A_CLASS, Object>>?

Примеры методов:

  public static Boolean toBoolean(String value) {
    // derive bool
  }

  public static Enum toEnum(String value, Class<?> fieldType) {
    // derive enum
  }

Ответы [ 2 ]

0 голосов
/ 07 апреля 2020

Любые предложения о том, как что-то вроде:

Map<Class<?>, WhateverFunction<String, MAYBE_A_CLASS, Object>>

Всегда делайте его BiFunction и просто игнорируйте параметр Class<?>:

Map<Class<?>, BiFunction<String, Class<?>, Object>> map = Map.of(
        Boolean.class, (s,t) -> Util.toBoolean(s),
        Enum.class, Util::toEnum
);

Поскольку пользователь карты не будет знать, нужен ли параметр Class<?>, он должен всегда передавать значение, например,

Class<?> type = ...
String value = ...

Object obj = map.get(type).apply(value, type);

Упрощенный. Обычно вы проверяете на null после вызова get().

Конечно, это тоже не сработает, потому что вызывающий объект думает, что типом является, например, DayOfWeek, поэтому, если вызывающий делает type = DayOfWeek.class, поиск не удастся. Теперь вдруг логика c, необходимая вызывающей стороне, становится сложной.

Решением для этого является инкапсуляция get-apply logi c вспомогательным методом, так что вы можете заменить DayOfWeek.class на Enum.class для поиска по карте, но если вы это сделаете, вы можете просто встроить прямую поддержку перечислений, минуя карту, и оставить карту с Function.

0 голосов
/ 07 апреля 2020

Зачем вам нужно имя класса для перечислений?

Например, DayOfWeek - это enum, и, как и любой другой класс enum, оно имеет valueOf(String name) метод, поэтому просто зарегистрируйте это:

DayOfWeek.class, DayOfWeek::valueOf

Если вам не нравится метод valueOf, например, потому что он чувствителен к регистру, и ваш метод toEnum лучше , затем напишите вспомогательный метод для инкапсуляции, например,

public static <T extends Enum<T>> Function<String, T> getEnumParser(Class<T> type) {
    return s -> toEnum(s, type);
}

public static <T extends Enum<T>> T toEnum(String value, Class<T> type) {
    for (T constant : type.getEnumConstants())
        if (constant.name().equalsIgnoreCase(value))
            return constant;
    throw new IllegalArgumentException("Unknown constant for enum " + type.getName() + ": \"" + value + "\"");
}

Затем вы зарегистрируете его, используя:

DayOfWeek.class, Util.getEnumParser(DayOfWeek.class)

Помощник хорош, но он вам на самом деле не нужен:

DayOfWeek.class, s -> Util.toEnum(s, DayOfWeek.class)

за исключением того, что это может сбить с толку компилятор при использовании с ImmutableMap.of(...). Помощник исправляет эту потенциальную проблему вывода компилятора.

...