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

Метод, принимающий два разных типа в качестве параметра

Я пишу метод, который должен принимать в качестве своего параметра объект одного из двух типов, которые не разделяют родительский тип, отличный от Object. Например, типы - это Мечты и Чеснок. Вы можете сделать как dreams.crush(), так и garlic.crush(). Я хочу иметь метод utterlyDestroy(parameter), который будет принимать в качестве параметра как Dreams, так и Garlic.

utterlyDestroy(parameter) {
    parameter.crush()
}

Оба чеснока и сны являются частью некоторой библиотеки, поэтому, если они реализуют интерфейс ICrushable (чтобы я мог писать utterlyDestroy(ICrushable parameter)), это не вариант.

Тело моего метода довольно длинное, поэтому перегрузка будет означать дублирование кода. Некрасиво. Я уверен, что смогу использовать рефлексию и немного взломать класс. Некрасиво.

Я пробовал использовать generics, но, видимо, я не могу написать что-то вроде

utterlyDestroy(<T instanceof Dreams || T instanceof Garlic> parameter)

Можно ли вывести Чеснок в мечты?

utterlyDestroy(Object parameter) {
    ((Dreams)parameter).crush()
}

Это все равно было бы уродливо. Каковы мои другие варианты и каков предпочтительный метод решения этой проблемы?

4b9b3361

Ответ 1

Как насчет этого:

interface ICrushable {
    void crush();
}

utterlyDestroy(ICrushable parameter) {
    // Very long crushing process goes here
    parameter.crush()
}

utterlyDestroy(Dreams parameter) {
    utterlyDestroy(new ICrushable() { crush() {parameter.crush();});
}

utterlyDestroy(Garlic parameter) {
    utterlyDestroy(new ICrushable() { crush() {parameter.crush();});
}

Новая разработка должна реализовывать интерфейс ICrushable, но для существующих классов параметр заверяется в ICrushable и передается в utterlyDestroy (ICrushable), который выполняет всю работу.

Ответ 2

Как насчет чего-то такого простого?

utterlyDestroy(Object parameter) {    
    if(parameter instanceOf Dreams){  
        Dream dream = (Dreams)parameter;  
        dream.crush();
        //Here you can use a Dream 
    }  
    else if(parameter instanceOf Garlic){  
       Garlic garlic = (Garlic)parameter;   
        //Here you can use a Garlic  
       garlic.crush();  
   }
} 

Если utterlyDestroy слишком сложный и большой, и вы просто хотите вызвать crush, тогда это сделает то, что вы хотите

Ответ 3

Вы можете реализовать Haskell-esque Either-class в Java; что-то вроде этого:

class Either<L,R>
{
    private Object value;

    public static enum Side {LEFT, RIGHT}

    public Either(L left)  {value = left;}
    public Either(R right) {value = right;}

    public Side getSide() {return value instanceof L ? Side.LEFT : Side.RIGHT;}

    // Both return null if the correct side isn't contained.
    public L getLeft() {return value instanceof L ? (L) value : null;}
    public R getRight() {return value instanceof R ? (R) value : null;}
}

Затем вы позволяете этому методу взять что-то типа Either<Dreams, Garlic>.

Ответ 4

Просто используйте перегрузку метода.

public void utterlyDestroy(Dreams parameter) {
    parameter.crush();
}

public void utterlyDestroy(Garlic parameter) {
    parameter.crush();
}

Если вы хотите поддерживать более двух этих типов таким же образом, вы можете определить общий интерфейс для всех и использовать дженерики.

Ответ 5

Вы можете использовать интерфейс и adapt ваши типы.

Интерфейс:

public interface Crushable {
  public void crush();
}

Пример вызова:

public class Crusher {
  public static void crush(Crushable crushable) {
    crushable.crush();
  }
}

Пример адаптера factory:

public final class Dreams {
  public static Crushable asCrushable(final Dream dream) {
    class DreamCrusher implements Crushable {
      @Override
      public void crush() {
        dream.crush();
      }
    }
    return new DreamCrusher();
  }

  private Dreams() {}
}

Код пользователя выглядит следующим образом:

  Dream dream = new Dream();
  Crushable crushable = Dreams.asCrushable(dream);
  Crusher.crush(crushable);

Если у вас есть много типов для адаптации, вы можете подумать об этом. Вот (неоптимизированный) адаптер factory, который использует тип Proxy:

public final class Crushables {
  private static final Class<?>[] INTERFACES = { Crushable.class };

  public static Crushable adapt(final Object crushable) {
    class Handler implements InvocationHandler {
      @Override
      public Object invoke(Object proxy, Method method, Object[] args)
          throws Throwable {
        return crushable.getClass()
            .getMethod(method.getName(), method.getParameterTypes())
            .invoke(crushable, args);
      }
    }

    ClassLoader loader = Thread.currentThread()
        .getContextClassLoader();
    return (Crushable) Proxy.newProxyInstance(loader, INTERFACES, new Handler());
  }

  private Crushables() {}
}

Для потребителя API это не так уродливо:

  Dream dream = new Dream();
  Crushable crushable = Crushables.adapt(dream);
  Crusher.crush(crushable);

Однако, как обычно с отражением, вы жертвуете проверкой типа времени компиляции.

Ответ 6

Создание интерфейса Crushable кажется самым чистым способом. Является ли подтипирование Garlic или Dreams опцией и добавление вашего интерфейса в подтип?

Запрет на то, что вы можете поместить общий код в частный метод и иметь две версии utterlyDestroy, сделать то, что они должны делать с отдельными объектами, прежде чем вызывать общий код. Если тело метода длинное, вероятно, необходимо разбить его на частные методы. Я предполагаю, что вы уже подумали об этом, хотя это еще более очевидно решение, чем добавление интерфейса.

Вы можете ввести параметр как объект и затем применить его. Это то, что вы подразумеваете под рефлексией? т.е.

public void utterlyCrush(Object crushable) {
    if (crushable instanceOf Dream) {
         ...
    }
    if (curshable instanceOf Garlic) {
         ...
    }

Но отбрасывание из Garlic to Dream не является опцией, учитывая, что он не является подтипом другого.

Ответ 7

Если вы собираетесь относиться к ним одинаково во многих местах вашего проекта, я предлагаю обернуть их в класс, что-то вроде адаптера.

Ответ 8

Как я использую:

void fooFunction(Object o){
Type1 foo=null;
if(o instanceof Type1) foo=(Type1)o;
if(o instanceof Type2) foo=((Type2)o).toType1();
// code
}

Но это работает только в том случае, если Type2 можно преобразовать в Type1