Изменение типа возврата в зависимости от метода вызова - PullRequest
4 голосов
/ 28 мая 2009

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

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

public Map<Type1, Type3> doSomething1();
public Map<Type2, Type3> doSomething2();

private Map<Type1/Type2, Type3> doSomething(); 

Итак, в приведенном выше примере doSomething () возвращает либо Type1, либо Type2 в качестве типа ключа Map, в зависимости от того, какой открытый метод вызвал его. Он сможет выполнить простую проверку и заполнить карту объектами правильного типа.

Может быть, это можно сделать с помощью умного отражения Java? Я не уверен. Все это выглядит довольно хитроумно, поэтому, если есть гораздо лучший способ сделать это, я весь в ушах.

Ответы [ 7 ]

8 голосов
/ 28 мая 2009

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

Лучшим способом сделать то же самое было бы преобразование doSomething () в меньшие функции, создание двух новых функций, называемых doSomethingFor1 () и doSomethingFor2 (). Обе эти функции могут повторно использовать измененные части старого doSomething ().

Теперь вызовите doSomething1 () и используйте doSomethingFor1 ().

Аналогично, doSomething2 () должен использовать doSomethingFor2 ().

ура

JRH.

3 голосов
/ 28 мая 2009

Обратите внимание: Я неправильно понял вопрос, так что этот ответ противоположен тому, о чем вопрос спрашивает. Я оставлю это для справки в вики сообщества, но это не отвечает на первоначальный вопрос.

Если бы существовал общий подтип для Type1 и Type2, называемый SuperType, тогда было бы сказано, что первый тип - ? extends SuperType.

Вот небольшой пример, в котором Integer и Double используются в качестве двух типов, а их общий предок - Number:

private Map<Integer, String> doSomethingForInteger() {
  HashMap<Integer, String> map = new HashMap<Integer, String>();
  map.put(10, "Hello");
  return map;
}

private Map<Double, String> doSomethingForDouble() {
  HashMap<Double, String> map = new HashMap<Double, String>();
  map.put(3.14, "Apple");
  return map;
}

public Map<? extends Number, String> doSomething(boolean b) {
  if (b)
    return doSomethingForInteger();
  else
    return doSomethingForDouble();
}

Здесь метод doSomething вернет два типа HashMap с в зависимости от переданного boolean. Метод doSomething возвращает HashMap<Integer, String> или HashMap<Double, String>.

На самом деле вызов doSomething с boolean может быть выполнен следующим образом:

Map<? extends Number, String> map1 = doSomething(true);
Map<? extends Number, String> map2 = doSomething(false);

map1 закончится с Hashmap<Integer, String>, а map2 получит Hashmap<Double, String>.

1 голос
/ 28 мая 2009

Мне нужно немного информации, которая отсутствует в вашем вопросе, например, например, как создаются экземпляры type1 и type 2, поскольку вы не можете создавать экземпляры напрямую из параметров произвольного типа. Теперь мы предполагаем, что есть некоторые общие данные, используемые для создания экземпляров как type1, так и type2. В противном случае, нет веских причин использовать один метод для обоих. В последнем случае это будет означать, что одна функция выполняет две разные вещи в зависимости от типа, что является плохим программированием. Если вы собираетесь использовать логику, которая напрямую основана на вызывающем методе, просто создайте два закрытых метода и выполните рефакторинг общих вещей.

Если вы выполняете только инстанцирование по-разному, основываясь на некоторых данных, самый простой способ сделать это - объявить закрытый внутренний интерфейс:

private interface Instantiator<T> {

     T getNew(Data data);        

}

Теперь, предполагая, что вы можете создавать экземпляры своих типов, вы можете использовать это:

private <T> Map<T,Type3> doSomething(Instantiator<T> instantiator) {...}

в качестве сигнатуры метода и вызовите ее для открытых методов с соответствующей реализацией Instantiator.

1 голос
/ 28 мая 2009

Первый вопрос: зачем вам такая точная структура?

Но безопасный способ вернуть его

public Map<Object, Type3> doSomething();

если нет общего супертипа для типов1 и 2. Если у вас есть общий супер тип, вы можете использовать его вместо object.

Было бы лучше, если бы рефакторинг кода не требовался.

1 голос
/ 28 мая 2009

Если ничего не помогает, пусть закрытый метод возвращает Object и использует приведение в открытых методах.

0 голосов
/ 28 мая 2009

Вы можете смоделировать идею закрытия:

interface MappingClosure {
    public void map(Map, MapProvider);
}


class Caller implements MappingClosure {
     public void map(final Map MAP, final MapProvider CALLEE) {
         this.MAP = specialise(MAP);
         // CALLEE USED AS KEY
     }
}


class MapProvider {  
    public Map getMap(final MappingClosure CALLER) {
        return CALLER.map(mapInstance, this);
    }
}

Тогда изменение данных в соответствии с потребностями объектов отличается от метода, в котором содержится объект, которому он нужен.

0 голосов
/ 28 мая 2009

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

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