Вот пример этого, который использует простой объект для каждого случая.
package mcve.util;
import java.util.*;
import java.util.function.*;
/**
* Allows switch-like statements with classes and consumers.
*/
public final class ClassSwitch implements Consumer<Object> {
/**
* For each of the specified cases, in order of their
* appearance in the array, if cases[i].test(obj) returns
* true, then invoke cases[i].accept(obj) and return.
*
* @param obj the object to switch upon
* @param cases the cases for the switch
* @throws NullPointerException
* if any of the cases are null
*/
public static void cswitch(Object obj, Case<?>... cases) {
if (cases != null) {
for (Case<?> c : cases) {
if (c.test(obj)) {
c.accept(obj);
break;
}
}
}
}
/**
* @param type the type of the case
* @param action the action to perform
* @param <T> the type of the case
* @throws NullPointerException
* if the type or action is null
* @return a new Case
*/
public static <T> Case<T> ccase(Class<T> type, Consumer<? super T> action) {
return new Case<>(type, action);
}
/**
* @param <T> the type of the case
*/
public static final class Case<T> implements Predicate<Object>,
Consumer<Object> {
private final Class<T> type;
private final Consumer<? super T> action;
/**
* @param type the type of the case
* @param action the action to perform
* @throws NullPointerException
* if the type or action is null
*/
public Case(Class<T> type, Consumer<? super T> action) {
this.type = Objects.requireNonNull(type, "type");
this.action = Objects.requireNonNull(action, "action");
}
/**
* @param obj the object to test
* @return true if the object is an instance of T, else false
*/
@Override
public boolean test(Object obj) {
return type.isInstance(obj);
}
/**
* @param obj the object to perform the action on
* @throws ClassCastException
* if the object is not an instance of T
*/
@Override
public void accept(Object obj) {
action.accept(type.cast(obj));
}
}
/**
* An unmodifiable list of the cases in this switch.
*/
private final List<Case<?>> cases;
/**
* @param cases the cases for this switch
* @throws NullPointerException
* if any of the cases are null
*/
public ClassSwitch(Case<?>... cases) {
if (cases == null) {
this.cases = Collections.emptyList();
} else {
List<Case<?>> list = new ArrayList<>(cases.length);
for (Case<?> c : cases) {
list.add(Objects.requireNonNull(c, "case"));
}
this.cases = Collections.unmodifiableList(list);
}
}
/**
* @return an unmodifiable view of the cases in this switch
*/
public List<Case<?>> getCases() { return cases; }
/**
* For each of the cases in this switch, in order of their
* appearance in the list, if cases.get(i).test(obj) returns
* true, then invoke cases.get(i).accept(obj) and return.
*
* @param obj the object to switch upon
*/
@Override
public void accept(Object obj) {
for (Case<?> c : cases) {
if (c.test(obj)) {
c.accept(obj);
break;
}
}
}
}
Пример использования будет примерно таким, предполагая импорт, например. import static mcve.util.ClassSwitch.*;
:
cswitch(anObject,
ccase(Byte.class, b -> System.out.println("Byte")),
ccase(Short.class, s -> System.out.println("Short")),
ccase(Integer.class, i -> System.out.println("Integer")),
ccase(Long.class, l -> System.out.println("Long")),
ccase(Float.class, f -> System.out.println("Float")),
ccase(Double.class, d -> System.out.println("Double"))
);
Вы также можете создать повторно используемый объект:
ClassSwitch ts =
new ClassSwitch(ccase(String.class, System.out::println),
ccase(Double.class, System.out::println));
ts.accept(anObject);
Примечания:
Если вам нужен случай default
, вы можете использовать Object.class
в качестве последнего случая.
Невозможно создать случай, который обрабатывает null
, но его можно немного изменить для этого. Вы могли бы, например. создайте class NullCase
, чей метод test
возвращает obj == null
.
Что вы также можете сделать, так это фактически генерировать перегрузки вместо использования varargs. Это позволяет вам связывать классы с потребителями, используя только общие объявления методов. Ниже приведен довольно простой пример этого:
package mcve.util;
import java.util.*;
import java.util.function.*;
/**
* Allows switch-like statements with classes and consumers.
*/
public final class GeneratedClassSwitch {
private GeneratedClassSwitch() {}
/**
* Generates overloads for a class switch to System.out.
*
* For example, if max=4, then 5 methods are generated:
* with 0, 1, 2, 3, and 4 cases.
*
* @param max
* the number of cases in the largest overload generated
* @param indents
* the number of indents to indent each generated method
* @throws IllegalArgumentException
* if max is negative or greater than 26, or if indents
* is negative
*/
public static void generateFixedOverloads(int max, int indents) {
if (max < 0 || max > 26) {
throw new IllegalArgumentException("max=" + max);
}
String indent = String.join("", Collections.nCopies(indents, " "));
for (int i = 0; i <= max; ++i) {
System.out.print(indent);
System.out.print("public static ");
if (i > 0) {
System.out.print("<");
for (char ch = 'A'; ch < 'A' + i; ++ch) {
if (ch != 'A') {
System.out.print(", ");
}
System.out.print(ch);
}
System.out.print("> ");
}
System.out.print("void cswitch");
if (i > 0) {
System.out.println();
System.out.print(indent + " (Object o, ");
for (char ch = 'A'; ch < 'A' + i; ++ch) {
if (ch != 'A') {
System.out.println(",");
System.out.print(indent + " ");
}
System.out.print("Class<" + ch + "> class" + ch);
System.out.print(", Consumer<? super " + ch + "> action" + ch);
}
} else {
System.out.print("(Object o");
}
System.out.println(") {");
for (char ch = 'A'; ch < 'A' + i; ++ch) {
if (ch == 'A') {
System.out.print(indent + " ");
} else {
System.out.print(" else ");
}
System.out.println("if (class" + ch + ".isInstance(o)) {");
System.out.print(indent + " ");
System.out.println("action" + ch + ".accept(class" + ch + ".cast(o));");
System.out.print(indent + " ");
System.out.print("}");
if (ch == ('A' + i - 1)) {
System.out.println();
}
}
System.out.print(indent);
System.out.println("}");
}
}
// Generated code pasted below.
public static void cswitch(Object o) {
}
public static <A> void cswitch
(Object o, Class<A> classA, Consumer<? super A> actionA) {
if (classA.isInstance(o)) {
actionA.accept(classA.cast(o));
}
}
public static <A, B> void cswitch
(Object o, Class<A> classA, Consumer<? super A> actionA,
Class<B> classB, Consumer<? super B> actionB) {
if (classA.isInstance(o)) {
actionA.accept(classA.cast(o));
} else if (classB.isInstance(o)) {
actionB.accept(classB.cast(o));
}
}
public static <A, B, C> void cswitch
(Object o, Class<A> classA, Consumer<? super A> actionA,
Class<B> classB, Consumer<? super B> actionB,
Class<C> classC, Consumer<? super C> actionC) {
if (classA.isInstance(o)) {
actionA.accept(classA.cast(o));
} else if (classB.isInstance(o)) {
actionB.accept(classB.cast(o));
} else if (classC.isInstance(o)) {
actionC.accept(classC.cast(o));
}
}
public static <A, B, C, D> void cswitch
(Object o, Class<A> classA, Consumer<? super A> actionA,
Class<B> classB, Consumer<? super B> actionB,
Class<C> classC, Consumer<? super C> actionC,
Class<D> classD, Consumer<? super D> actionD) {
if (classA.isInstance(o)) {
actionA.accept(classA.cast(o));
} else if (classB.isInstance(o)) {
actionB.accept(classB.cast(o));
} else if (classC.isInstance(o)) {
actionC.accept(classC.cast(o));
} else if (classD.isInstance(o)) {
actionD.accept(classD.cast(o));
}
}
public static <A, B, C, D, E> void cswitch
(Object o, Class<A> classA, Consumer<? super A> actionA,
Class<B> classB, Consumer<? super B> actionB,
Class<C> classC, Consumer<? super C> actionC,
Class<D> classD, Consumer<? super D> actionD,
Class<E> classE, Consumer<? super E> actionE) {
if (classA.isInstance(o)) {
actionA.accept(classA.cast(o));
} else if (classB.isInstance(o)) {
actionB.accept(classB.cast(o));
} else if (classC.isInstance(o)) {
actionC.accept(classC.cast(o));
} else if (classD.isInstance(o)) {
actionD.accept(classD.cast(o));
} else if (classE.isInstance(o)) {
actionE.accept(classE.cast(o));
}
}
public static <A, B, C, D, E, F> void cswitch
(Object o, Class<A> classA, Consumer<? super A> actionA,
Class<B> classB, Consumer<? super B> actionB,
Class<C> classC, Consumer<? super C> actionC,
Class<D> classD, Consumer<? super D> actionD,
Class<E> classE, Consumer<? super E> actionE,
Class<F> classF, Consumer<? super F> actionF) {
if (classA.isInstance(o)) {
actionA.accept(classA.cast(o));
} else if (classB.isInstance(o)) {
actionB.accept(classB.cast(o));
} else if (classC.isInstance(o)) {
actionC.accept(classC.cast(o));
} else if (classD.isInstance(o)) {
actionD.accept(classD.cast(o));
} else if (classE.isInstance(o)) {
actionE.accept(classE.cast(o));
} else if (classF.isInstance(o)) {
actionF.accept(classF.cast(o));
}
}
public static <A, B, C, D, E, F, G> void cswitch
(Object o, Class<A> classA, Consumer<? super A> actionA,
Class<B> classB, Consumer<? super B> actionB,
Class<C> classC, Consumer<? super C> actionC,
Class<D> classD, Consumer<? super D> actionD,
Class<E> classE, Consumer<? super E> actionE,
Class<F> classF, Consumer<? super F> actionF,
Class<G> classG, Consumer<? super G> actionG) {
if (classA.isInstance(o)) {
actionA.accept(classA.cast(o));
} else if (classB.isInstance(o)) {
actionB.accept(classB.cast(o));
} else if (classC.isInstance(o)) {
actionC.accept(classC.cast(o));
} else if (classD.isInstance(o)) {
actionD.accept(classD.cast(o));
} else if (classE.isInstance(o)) {
actionE.accept(classE.cast(o));
} else if (classF.isInstance(o)) {
actionF.accept(classF.cast(o));
} else if (classG.isInstance(o)) {
actionG.accept(classG.cast(o));
}
}
public static <A, B, C, D, E, F, G, H> void cswitch
(Object o, Class<A> classA, Consumer<? super A> actionA,
Class<B> classB, Consumer<? super B> actionB,
Class<C> classC, Consumer<? super C> actionC,
Class<D> classD, Consumer<? super D> actionD,
Class<E> classE, Consumer<? super E> actionE,
Class<F> classF, Consumer<? super F> actionF,
Class<G> classG, Consumer<? super G> actionG,
Class<H> classH, Consumer<? super H> actionH) {
if (classA.isInstance(o)) {
actionA.accept(classA.cast(o));
} else if (classB.isInstance(o)) {
actionB.accept(classB.cast(o));
} else if (classC.isInstance(o)) {
actionC.accept(classC.cast(o));
} else if (classD.isInstance(o)) {
actionD.accept(classD.cast(o));
} else if (classE.isInstance(o)) {
actionE.accept(classE.cast(o));
} else if (classF.isInstance(o)) {
actionF.accept(classF.cast(o));
} else if (classG.isInstance(o)) {
actionG.accept(classG.cast(o));
} else if (classH.isInstance(o)) {
actionH.accept(classH.cast(o));
}
}
}
Если вы хотите сгенерировать перегрузки, например, иметь более 8 случаев, вы можете сказать что-то вроде следующего:
GeneratedClassSwitch.generateFixedOverloads(16, 1);
Это создаст методы для System.out
, которые следуют общей форме:
public static <A, B, C> void cswitch
(Object o, Class<A> classA, Consumer<? super A> actionA,
Class<B> classB, Consumer<? super B> actionB,
Class<C> classC, Consumer<? super C> actionC) {
if (classA.isInstance(o)) {
actionA.accept(classA.cast(o));
} else if (classB.isInstance(o)) {
actionB.accept(classB.cast(o));
} else if (classC.isInstance(o)) {
actionC.accept(classC.cast(o));
}
}
Обратите внимание, что мы можем сопоставить каждый тип класса со связанным с ним типом потребителя, т. е. Class<A>
с Consumer<? super A>
, Class<B>
с Consumer<? super B>
и так далее. На самом деле это невозможно с varargs (во всяком случае, в текущей версии Java, которая равна 10).
Наш пример использования теперь такой, опять же при условии импорта, например. import static mcve.util.GeneratedClassSwitch.*;
:
cswitch(anObject,
Byte.class, b -> System.out.println("Byte"),
Short.class, s -> System.out.println("Short"),
Integer.class, i -> System.out.println("Integer"),
Long.class, l -> System.out.println("Long"),
Float.class, f -> System.out.println("Float"),
Double.class, d -> System.out.println("Double")
);
(Примечания о случаях default
и null
такие же, как и в первом примере.)
person
Radiodef
schedule
07.07.2018
handle()
? Почему бы не перегрузить его в своем собственном классе для каждого из классов? - person RealSkeptic   schedule 11.04.2015Map<Class<?>, Function<?, Void>>
или чего-то подобного? - person Luiggi Mendoza   schedule 11.04.2015getCanonicalName()
— это метод дляClass
, а неA
,B
и т. д. Вероятно, у вас должно быть что-то вродеpublic enum Classes { A (A.class.getCanonicalName()), ...
. При этом использование имен схематично, мне намного больше нравится решение @dasblinkenlight. - person azurefrog   schedule 11.04.2015if
иswitch
не эквивалентны из-за полиморфизма. Условиеo instanceof A
может быть истинным, даже еслиo.getClass()
не равноA
. - person Andy Thomas   schedule 11.04.2015