#java
#java #общие
Вопрос:
Я хочу создать определенный тип интерфейса в Java (хотя это так же применимо к обычным классам). Этот интерфейс должен был бы содержать некоторый метод, скажем, invoke
; он будет вызываться с различным количеством параметров в зависимости от предоставленных аргументов универсального типа.
В качестве примера:
public interface Foo<T...> {
public void invoke(T... args);
}
// In some other class
public static Foo<Float, String, Integer> bar = new Foo<Float, String, Integer>() {
@Override
public void invoke(Float arg1, String arg2, Integer arg3) {
// Do whatever
}
};
Чтобы кратко объяснить, как это можно использовать (и предоставить некоторый контекст), рассмотрим класс Delegator
: класс принимает различное количество универсальных типов и имеет один метод — invoke
, с этими типами параметров. Метод передает свои параметры объекту в списке: экземпляру IDelegate
, который принимает те же общие типы. Это позволяет Delegator
выбирать между несколькими методами делегирования (определенными внутри IDelegate
) без необходимости создавать новый класс для каждого конкретного списка типов параметров.
Доступно ли что-нибудь подобное? Я читал о шаблонах переменных в C , но не могу найти ничего подобного в Java. Доступна ли какая-либо такая вещь? Если нет, то каков был бы самый чистый способ эмуляции той же модели данных?
Ответ №1:
Доступно ли что-нибудь подобное? Я читал о шаблонах переменных в C , но не могу найти ничего подобного в Java. Доступна ли какая-либо такая вещь?
Нет, эта функция недоступна в Java.
Ответ №2:
Нет, ничего подобного напрямую не доступно. Однако, если вы используете библиотеку с Tuple
классами, вы можете имитировать ее, просто создав интерфейс
interface Foo<T> {
void invoke(T t);
}
(Этот интерфейс по сути такой же, как Consumer<T>
.)
Тогда вы могли бы сделать, например
Foo<Tuple<String, Integer, Date, Long>> foo = new Foo<>() {
...
}
Вам понадобится отдельный Tuple
тип для каждого количества параметров. Если у вас есть Tuple
класс для 4 параметров, но нет ни одного для 5, вы можете ввести дополнительный параметр с помощью Pair
класса.
Foo<Tuple<String, Integer, Date, Pair<Long, BigDecimal>>> foo = ...
Вложив типы кортежей таким образом, вы получаете неограниченное количество параметров. Однако эти обходные пути действительно уродливы, и я бы не стал их использовать.
Комментарии:
1. Иными словами:
Pair<S,T>
было бы достаточно. Но поскольку никто не хочет aPair<Integer,Pair<String,Pair<Byte,Pair<Float, Long>>>>
, подобные вещи обычно решаются по-другому. Либо полностью отбросив безопасность типов, либо с помощью таких библиотек, как javatuples.org
Ответ №3:
Учитывая предоставленный вами контекст, я бы рекомендовал использовать a List
в качестве параметра. Если у этих параметров есть что-то общее, вы можете ограничить свой список <T extends CommonParrent>
вместо использования List<Object>
. Если нет, вы все равно можете захотеть использовать интерфейс marker.
Вот пример.
public class Main {
public static void main(String[] args) {
delegate(asList(new ChildOne(1), new ChildTwo(5), new ChildOne(15)));
}
private static <T extends Parent> void delegate(List<T> list) {
list.forEach(item -> {
switch (item.type) {
case ONE: delegateOne((ChildOne) item); break;
case TWO: delegateTwo((ChildTwo) item); break;
default: throw new UnsupportedOperationException("Type not supported: " item.type);
}
});
}
private static void delegateOne(ChildOne childOne) {
System.out.println("child one: x=" childOne.x);
}
private static void delegateTwo(ChildTwo childTwo) {
System.out.println("child two: abc=" childTwo.abc);
}
}
public class Parent {
public final Type type;
public Parent(Type type) {
this.type = type;
}
}
public enum Type {
ONE, TWO
}
public class ChildOne extends Parent {
public final int x;
public ChildOne(int x) {
super(Type.ONE);
this.x = x;
}
}
public class ChildTwo extends Parent {
public final int abc;
public ChildTwo(int abc) {
super(Type.TWO);
this.abc = abc;
}
}
Самый большой недостаток этого решения заключается в том, что дочерние элементы должны указывать свой тип через enum, который должен соответствовать приведениям в операторе switch , поэтому всякий раз, когда вы меняете одно из этих двух мест, вам нужно будет помнить об изменении другого, потому что компилятор не скажет вам об этом. Вы обнаружите такую ошибку, только запустив код и выполнив определенную ветку, поэтому рекомендуется разработка на основе тестирования.