Вызов метода перегружен, когда все аргументы реализуют один и тот же интерфейс - PullRequest
1 голос
/ 13 января 2011

Моя отправная точка следующая:
- у меня есть метод transform, который я перегружал, чтобы вести себя по-разному, в зависимости от типа передаваемых аргументов (см. Transform (A a1, A a2) и transform (A a1, B b) в моем примере ниже)
- Все эти аргументы реализуют один и тот же интерфейс, X

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

Я придумал реализовать преобразование (X x1, X x2), которое проверяет экземпляр каждого объекта перед применением соответствующего варианта моего преобразования.

Хотя это работает, код кажетсяУжасно, и я также обеспокоен накладными расходами производительности для оценки этих различных экземпляров и приведения.Это преобразование - лучшее, что я могу сделать в Java, или есть более элегантный и / или эффективный способ достижения того же поведения?

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

public class A implements X {
}

public class B implements X {
}

interface X {
}

public A transform(A a1, A a2) {
  System.out.print("A");
  return a2;
}

public A transform(A a1, B b) {
  System.out.print("B");
  return a1;
}

// Isn't there something better than the code below???
public X transform(X x1, X x2) {
  if ((x1 instanceof A) && (x2 instanceof A)) {
    return transform((A) x1, (A) x2);
  } else if ((x1 instanceof A) && (x2 instanceof B)) {
    return transform((A) x1, (B) x2);
  } else {
    throw new RuntimeException("Transform not implemented for "
            + x1.getClass() + "," + x2.getClass());
  }
}

@Test
public void trivial() {
  X x1 = new A();
  X x2 = new B();
  X result = transform(x1, x2);
  transform(x1, result);
}

Ответы [ 3 ]

5 голосов
/ 13 января 2011

Взгляните на Шаблон посетителя в качестве отправной точки.

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

Код может выглядеть так:

public interface X {
  void accept(XVisitor v);
}

public interface XVisitor { 
  void visit(A a);
  void visit(B b);
}

public class A implements X {
  public void accept(XVisitor v) {
    v.visit(this);
  }
}

public class B implements X {
  public void accept(XVisitor v) {
    v.visit(this);
  }
}

И тогда ваш алгоритм переходит в этот класс:

public class XTransformerVisitor implements XVisitor {
  private X result;
  private A first;
  public void visit(A a) {
    if (first == null) first = a;
    else result = a;
  }
  public void visit(B b) {
    if (first == null) throw new RuntimeException();
    result = first;
  }
  public X transform(X x1, X x2) {
    x1.accept(this);
    x2.accept(this);
    return result;
  }
}
3 голосов
/ 13 января 2011

Вы ищете термин многократная диспетчеризация, , который является обобщением виртуальных функций, которые полиморфны в типах множественных аргументов.Большинство языков программирования, в том числе Java и C ++, не поддерживают множественную диспетчеризацию, поэтому для их эмуляции требуется какая-то хакерская программа.Один из вариантов - использовать код, подобный тому, который вы использовали выше, а другой - использовать что-то , например, .Одним из распространенных решений является использование идиомы, называемой шаблон посетителя , которая может помочь абстрагироваться от сложности.

0 голосов
/ 20 января 2011

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

Мой оригинальный код выше может остаться точно таким же, и преобразование раздражающего метода (X x1, X x2) просто становится:

public X transform(X x1, X x2) {
  Method m;
  try {
    m = getClass().getMethod("transform",
            new Class[]{x1.getClass(), x2.getClass()});
    return (X) m.invoke(this, new Object[]{x1, x2});
  } catch (Exception e) {
    throw new RuntimeException("Transform not implemented for "
        + x1.getClass() + "," + x2.getClass());
  }
}

Преимущества:
- избавляет от вложенных экземпляров тестов и приведений, которые я имел в моем первоначальном посте
- нет необходимости реализовывать метод accept, который должны реализовывать все операнды, привнесенныеподход двойной отправки / шаблон посетителя

...