Универсальная функция Java: как вернуть универсальный тип - PullRequest
13 голосов
/ 24 декабря 2009

Вот общий шаблон Java:

public <T> T getResultData(Class<T> resultClass, other_args) { 
   ...
   return resultClass.cast(T-thing);
}

Типичный вызов выглядит так:

   DoubleBuffer buffer;
   buffer = thing.getResultData(DoubleBuffer.class, args);

Мне никогда не удавалось понять, как правильно использовать этот шаблон, когда желаемый тип возврата сам по себе является общим. Чтобы быть «конкретным», что если подобная функция хочет вернуть Map<String,String>? Поскольку вы не можете получить объект класса для универсального, конечно, единственный вариант - передать Map.class, а затем вам понадобится приведение и @SuppressWarning в конце концов.

В итоге получается звонок вроде:

Map<String, String> returnedMap;
returnedMap = thing.getResultData(Map.class, some_other_args);

Теперь мы снова находимся в необходимости приведения и подавления предупреждения.

Я полагаю, что можно взять что-то из семейства java.lang.reflect.Type вместо Class, но это не особенно легко придумать. Это выглядит так:

class Dummy {
 Map<String, String> field;
}

...

Type typeObject = Dummy.class.getField("field").getGenericType();

Учитывая это, вызываемая функция может извлекать базовый тип и использовать его для приведения типов или newInstance-ing, но бизнес с фиктивными полями наверняка выглядит некрасиво.

Обратите внимание, что подобные функции не всегда вызывают newInstance. Очевидно, что если они это сделают, им не нужно звонить resultClass.cast.

Ответы [ 6 ]

7 голосов
/ 24 декабря 2009

Вы не можете сделать это в стандартном Java API. Тем не менее, есть доступные классы утилит, которые могут извлечь Type из универсального класса. Например, в Google Gson (который преобразует JSON в полноценные Javabeans и наоборот) есть класс TypeToken. Вы можете использовать его следующим образом:

List<Person> persons = new Gson().fromJson(json, new TypeToken<List<Person>>() {}.getType());

Такая конструкция именно то, что вам нужно. Вы можете найти здесь исходный код, вы также можете найти его полезным. Для этого требуется только дополнительный метод getResultData(), который может принимать Type.

2 голосов
/ 24 декабря 2009

Да, я не думаю, что это возможно.

Представьте себе этот сценарий. У вас есть файл с сериализованным объектом, который вы хотите прочитать. Если вы сделаете функцию универсальной, вы сможете использовать ее без приведения. Но допустим, у вас есть карта, сериализованная в файл.

Когда вы десериализуете его, невозможно узнать , каким был исходный универсальный тип карты. Что касается Java, это просто карта

Я столкнулся с этим со списками, и это раздражало. Но в действительности я закончил тем, что создал общий класс «конвертирующей коллекции», который принимает коллекцию и «конвертер» (который может быть анонимным внутренним типом).

Затем, когда вы перебираете конвертирующую коллекцию, каждый элемент получает роль. И это решило проблему предупреждения. Это большая работа, чтобы просто избавиться от предупреждения, но я не думаю, что это было главной причиной, по которой я придумал эту структуру. Вы также можете сделать более мощные вещи, например, взять набор имен файлов, а затем написать конвертер, который загружает данные в файл или выполняет запрос, или что-то еще.

2 голосов
/ 24 декабря 2009

Ну ,. вот некрасивое (частичное) решение. Вы не можете обобщенно параметризировать универсальный параметр, поэтому вы не можете написать

public <T<U,V>> T getResultData ...

но вы можете вернуть параметризованную коллекцию, например, за комплект

public < T extends Set< U >, U > T getResultData( Class< T > resultClass, Class< U > paramClass ) throws IllegalAccessException, InstantiationException {
        return resultClass.newInstance();
    }

где вы можете избавиться от U-параметра, если он существует в сигнатуре класса.

1 голос
/ 04 августа 2011

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

Map<?, ?> returnedMap;
returnedMap = thing.getResultData(Map.class, some_other_args);

тогда вы можете использовать любой из методов карты, не параметризованных по типу, таких как containsKey, clear, iterator и т. Д., Или выполнять итерацию по entrySet или передавать returnMap любому универсальному методу, и все будет безопасно для типа.

Map<?, ?> returnedMap;
returnedMap = thing.getResultData(Map.class, some_other_args);

Object myKey = ...;

Object someValue = returnedMap.get(myKey);

for (Iterator<? extends Map.Entry<?,?>> it = returnedMap.entrySet().iterator(); it.hasNext(); )
  if (it.next().equals(myKey))
    it.remove();

for (Map.Entry<?,?> e : returnedMap.entrySet()) {
  if (e.getKey().equals(myKey))
    e.setValue(null); // if the map supports null values
}

doSomething(returnedMap);

...

<K,V> void doSomething(Map<K,V> map) { ... }

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

1 голос
/ 24 декабря 2009

Так что, если я правильно понял, то, что вы действительно пытаетесь сделать (неправильный псевдосинтаксис):

public <T, S, V> T<S, V> getResultData(Class<T<S, V>> resultClass, other_args) { 
  ...
  return resultClass.cast(T-thing);
}

Вы не можете, потому что вы не можете далее параметризировать универсальный тип T. Бессмысленно с java.lang.reflect.* не поможет:

  1. Не существует такого понятия, как Class<Map<String, String>>, и, следовательно, нет способа динамически создать его экземпляр.
  2. Нет способа объявить рассматриваемый метод как возвращающий T<S, V>
1 голос
/ 24 декабря 2009

Вы можете использовать TypeLiteral , если хотите использовать классы безопасных контейнеров. Они используются в Guice для обеспечения безопасных привязок. См. Исходный код Guice для большего количества примеров и TypeLiteral class.

Кстати, вам не понадобится приведение, если вы, например, позвоните resultClass.newInstance().

public <T> T getResultData(Class<T> resultClass, other_args) {       
   ...
   return resultClass.newInstance();
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...