Подтвердить что ты не робот

Общий метод преобразования типа данных

Этот вопрос возникает в ответ на другой вопрос opensas: создание общей функции инициализации в java

Из его вопроса стало ясно, что ему нужно преобразовать из любого типа данных T1 в другой тип T2. Когда я говорю "тип данных" здесь, я имею в виду типы, ограниченные теми, которые обычно используются для представления необработанных данных: Integer, String, Date и т.д. В целях этого вопроса мы можем рассматривать примитивы, которые должны быть помещены в бокс.

Мне интересно, существует ли какой-либо API, который поддерживает преобразование между типами, где оба входа и вывода обобщаются на набор поддерживаемых типов данных. Я посмотрел пакет beanutils.converters Apache Commons, но есть отдельный класс преобразователя для каждого известного ввода. Я ищу любую функциональность, которая реализует что-то вроде следующей подписи:

static <IN, OUT> OUT convert(IN value, Class<OUT> targetType);

или еще

static <IN, OUT> OUT convert(IN value, OUT defaultValue);

На самом деле было бы не слишком сложно реализовать такой вид отображения, используя пучок блоков else if, указывающих на различные Commons Converters, или же Map<Class<?>, Converter> для этой же цели. Но мне интересно, поддерживается ли такая функциональность где-то.

Кроме того, если это приводит к дублированию, я приношу свои извинения. Я попытался найти похожие вопросы и был удивлен, когда не нашел подходящих для этой ситуации.

EDIT:, поэтому пример этого кода в действии:

Integer i = GenericConverter.convert("123", Integer.class);    //returns 123
Date d = GenericConverter.convert(1313381772316L, Date.class); //returns today date
Boolean b = GenericConverter.convert(0, Boolean.class);        //returns false
Long l = GenericConverter.convert("asdf", Long.class);         //RuntimeException

ОБНОВЛЕНИЕ: Скомпонуемый мной код BalusC близок к знаку, а богемский ответ - хорошее облегченное решение (хотя оно не работает для логических преобразований). Он также прав, что Dates, вероятно, следует обрабатывать отдельно, если мы хотим обобщить преобразование этих других типов данных. Я все еще надеюсь на дополнительные ответы, хотя, особенно если есть более доступный API-интерфейс.

4b9b3361

Ответ 1

В JDK 8 это можно легко реализовать с помощью нового интерфейса java.util.functions.Mapper и выражения лямбда.

Mapper<String,Integer> atoi = s -> Integer.valueOf(s);
Integer r = atoi.map("10");

Использование ссылок на методы может быть еще проще:

Mapper<String, Integer> atoi = Integer::new;
Integer r = atoi.map("10");

Или такие вещи, как:

List<Long> dates = asList(1344754620310L,1344754854877L);
List<Date> asDates = dates.map(Date::new).into(new ArrayList<Date>());

Или крутые конверсии, например:

List<Integer> myInts = "5,4,3,2,1,0,6,7,8,9"
  .splitAsStream(",")
  .map(Integer::new)
  .into(new ArrayList<Integer>());

В текущей реализации API JDK8 было определено несколько шаблонов по умолчанию (т.е. LongMapper, IntMapper, DoubleMapper) и там есть класс утилиты Mappers, который определяет некоторые другие, такие как строковый преобразователь, и сопоставление идентичности, постоянный картограф и т.д.

Я не уверен, что это то, что вам нужно, но, конечно, это хороший способ его реализации.

Случаи, подобные тем, которые вы предлагаете:

static <IN, OUT> OUT convert(IN value, Class<OUT> targetType);

Может быть реализован с помощью утилиты Mappers:

Mapper<String, Integer> atoi = Mappers.instantiate(String.class, Integer.class);
Integer r = atoi.map("10");

И ваша подпись:

static <IN, OUT> OUT convert(IN value, OUT default);

Может быть реализовано с чем-то вроде:

Mapper<String, Integer> atoi = chain(substitute(null, "0"), Integer::new);
Integer r = atoi.map(null); //produces 0

Как таковой, такой код...

List<String> data = asList("0", null, "2", null, "4", null, "6");
List<Integer> myInts = data.map(chain(substitute(null, "0"), Integer::new)).into(new ArrayList<Integer>());
System.out.println(myInts);

Допустим: [0, 0, 2, 0, 4, 0, 6]

Ответ 2

Мне неизвестна какая-либо библиотека, однако код - всего одна строка.

Помимо даты, все примитивы в штучной упаковке имеют String construtor, поэтому этот метод выполняет трюк:

public static <I, O> O convert(I input, Class<O> outputClass) throws Exception {
    return input == null ? null : outputClass.getConstructor(String.class).newInstance(input.toString());
}

Чтобы обслуживать даты, вы можете использовать instanceof в этом методе, но я бы рекомендовал отдельный метод, поскольку преобразование дат относится к форме и контексту (например, String → Date parses и использует какой формат?, Long → Date устанавливает время).

Я намеренно оставил ошибку/специальную обработку читателю в качестве упражнения.

Ответ 3

Взгляните на Variance, который позволяет вам настроить контекст преобразования типов с зарегистрированными различными преобразователями, а затем переместить значения в из типа Variant с преобразованием типов, обрабатываемым контекстом.

Variant aVariant = Variant.of("1.2345");
double aDouble = aVariant.doubleValue();

int anInt = Variant.of("12").intValue();
String aString = Variant.of(12.0).toString();
Date aDate = Variant.of("2012-04-06").as(Date.class);
String anIsoFormattedDate = Variant.of(aDate).in(isoDateFormattingContext).toString()

Конвертеры - это просто Guava Функции от одного тип к другому, и вы можете зарегистрировать свой собственный, переопределяя существующие преобразования, где это необходимо.

Ответ 4

Я нашел что-то от BalusC, который близок к тому, что я прошу: http://balusc.blogspot.com/2007/08/generic-object-converter.html

К сожалению, ничего, связанное с преобразованием даты, не поддерживается, но, как показывают комментарии, легко добавлять дополнительные методы преобразования. Его класс по существу представляет собой небольшую небольшую структуру, которая использует отражение для сбора всех методов преобразования во время выполнения и помещает их в HashMap<String, Method>, где ключ String является уникальным идентификатором для этой комбинации ввода-вывода.

Ищем другие предложения! В частности, для API, который будет больше рук, чем этот код, с которым я связан.

Ответ 5

Если вы используете Spring Framework (spring -core), вы можете использовать класс

org.springframework.core.convert.support.DefaultConversionService

Конструктор по умолчанию добавляет много типов конвертеров, и вы можете добавить свой собственный, реализуя интерфейс конвертера и вызывая addConverter (конвертер). Существует также приятный unit test, показывающий некоторые комбинации конверсий.