Переключить тип в java

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

У меня есть метод, который принимает суперкласс и вызывает метод на основе типа переданного объекта. например:

public void handle(Object o){
  if (o instanceof A)
    handleA((A)o);
  else if (o instanceof B)
    handleB((B)o);
  else if (o instanceof C)
    handleC((C)o);
  else 
    handleUnknown(o);

Я не могу изменить подтипы, чтобы переопределить метод handle(), как предлагает этот ответ, потому что Я не владею классами. Так что метод instanceof — это все, что у меня есть.

Я хотел бы использовать оператор switch вместо if/else просто потому, что он намного аккуратнее. Я знаю, что вы можете включать только примитивы и строки, поэтому я переключаю имя класса:

switch(o.getClass().getCanonicalName()){
case "my.package.A":
  handleA((A)o);
  break;
case "my.package.B":
  handleB((B)o);
  break;
case "my.package.C":
  handleC((C)o);
  break;
default:
  handleUnknown(o);
  break;
}

Загвоздка здесь в том, что канонические имена ОЧЕНЬ длинные (например, 12 подпакетов), и я не могу вызвать ClassName.class.getCanonicalName() в операторе case, потому что Java этого не позволяет. Итак, моим следующим решением был Enum. Здесь я столкнулся со своей проблемой.

Я бы хотел, чтобы мой код выглядел примерно так:

public enum Classes {
  A (A.getCanonicalName()),
  B (B.getCanonicalName()),
  C (C.getCanonicalName());
}

switch (o.getClass().getCanonicalName()){
case Classes.A:
  handleA((A)o);
  break;
case Classes.B:
  handleB((B)o);
  break;
case Classes.C:
  handleC((C)o);
  break;
default:
  handleUnknown(o);
  break;
}

Но это не компилируется. Я не уверен, почему. Мне нужен какой-то подход, который позволяет мне переключать тип без необходимости вводить все каноническое имя. Если я это сделаю, я могу просто использовать if/else и instanceof.

ПРИМЕЧАНИЕ Существует несколько типов с одинаковыми именами (внутренние классы), поэтому getSimpleName() отсутствует.


person ewok    schedule 10.04.2015    source источник
comment
Какую ошибку вы получаете, когда пытаетесь скомпилировать?   -  person azurefrog    schedule 11.04.2015
comment
Переключатель не намного аккуратнее. Это гораздо опаснее и часто является источником ошибок. Вы попали в ловушку или забыли добавить перерыв для каждого случая.   -  person JB Nizet    schedule 11.04.2015
comment
Как будет называться handle()? Почему бы не перегрузить его в своем собственном классе для каждого из классов?   -  person RealSkeptic    schedule 11.04.2015
comment
Как насчет использования Map<Class<?>, Function<?, Void>> или чего-то подобного?   -  person Luiggi Mendoza    schedule 11.04.2015
comment
@JBNizet хорошая мысль. Но ошибка компиляции связана с неправильным определением Enum. И под более аккуратным я имел в виду более читабельный.   -  person ewok    schedule 11.04.2015
comment
Одна вещь, которая бросается в глаза, заключается в том, что getCanonicalName() — это метод для Class, а не A, B и т. д. Вероятно, у вас должно быть что-то вроде public enum Classes { A (A.class.getCanonicalName()), .... При этом использование имен схематично, мне намного больше нравится решение @dasblinkenlight.   -  person azurefrog    schedule 11.04.2015
comment
Ваши связанные операторы if и switch не эквивалентны из-за полиморфизма. Условие o instanceof A может быть истинным, даже если o.getClass() не равно A.   -  person Andy Thomas    schedule 11.04.2015
comment
@ewok ИМХО, как только вы добавите все разрывы и поймете, что для понимания кода вам нужно внимательно посмотреть, присутствует ли разрыв или нет, вы поймете, что цепочка if / else if на самом деле больше удобочитаемый.   -  person JB Nizet    schedule 11.04.2015


Ответы (9)


Вот подход, который вообще не имеет дело с именами классов и выполняет диспетчеризацию так же быстро, как оператор switch: создайте хэш-карту для сопоставления объектов Class<T> с обработчиками, специфичными для класса, и используйте карту вместо switch:

// Declare an interface for your polymorphic handlers to implement.
// There will be only anonymous implementations of this interface.
private interface Handler {
    void handle(Object o);
}
// Make a map that translates a Class object to a Handler
private static final Map<Class,Handler> dispatch = new HashMap<Class,Handler>();
// Populate the map in a static initializer
static {
    dispatch.put(A.class, new Handler() {
        public void handle(Object o) {
            handleA((A)o);
        }
    });
    dispatch.put(B.class, new Handler() {
        public void handle(Object o) {
            handleB((B)o);
        }
    });
    dispatch.put(C.class, new Handler() {
        public void handle(Object o) {
            handleC((C)o);
        }
    });
}
// This object performs the dispatch by looking up a handler,
// and calling it if it's available
private static void handle(Object o) {
    Handler h = dispatch.get(o.getClass());
    if (h == null) {
        // Throw an exception: unknown type
    }
    h.handle(o); // <<== Here is the magic
}
person Sergey Kalinichenko    schedule 10.04.2015
comment
Что произойдет, если это подпитка подкласса A? ОП instanceof позвонил бы handleA(). - person Andy Thomas; 11.04.2015
comment
@AndyThomas То же самое, что и в заявлении OP switch :) - person Sergey Kalinichenko; 11.04.2015
comment
С лямбдами или ссылками на методы Java 8 вы можете сделать это намного более кратким (нет необходимости во всех анонимных Handler подклассах). - person Jesper; 11.04.2015
comment
@Jesper Это определенно правда. Однако я не уверен, разрешено ли OP использовать Java 8. - person Sergey Kalinichenko; 11.04.2015

Используя лямбды java 8, вы можете получить что-то вроде этого:

Collection col = Arrays.asList(1,2,3);
switchType(col, 
       caze(Collection.class, c->System.out.println(c.size())),
       caze(ArrayBlockingQueue.class, bq->System.out.println(bq.remainingCapacity())),
       caze(Queue.class, q->System.out.println(q.poll())),
       caze(String.class, s->System.out.println(s.substring(0))),
       caze(ArrayList.class, al->System.out.println(al.get(0)))
);

Для этого вы должны определить следующие статические методы:

public static <T> void switchType(Object o, Consumer... a) {
    for (Consumer consumer : a)
        consumer.accept(o);
}

public static <T> Consumer caze(Class<T> cls, Consumer<T> c) {
    return obj -> Optional.of(obj).filter(cls::isInstance).map(cls::cast).ifPresent(c);
}    
person eitan    schedule 19.05.2016
comment
Сработало, хорошо, но как break я имею в виду, если первый матч называется, а затем выйти из Consumer - person Al-Mothafar; 14.12.2016
comment
Самое элегантное решение! - person Joel Shemtov; 16.04.2017
comment
@eitan Это отличная мысль, но как я могу использовать return для каждого случая? - person 2Big2BeSmall; 12.07.2017
comment
это добавляет много лишнего - person babay; 25.08.2017
comment
@Francy, вы можете использовать функции вместо потребителей. - person eitan; 04.09.2017

В настоящее время у Java есть черновик для поддержки этого. См. здесь. Синтаксис выглядит так

switch (obj) {
    case Integer i: handleI(i); break;
    case Byte b:    handleB(b); break;
    case Long l:    handleL(l); break;
    case Double d:  handleD(d); break;
    case String s:  handleS(s); break
    default:        handle(obj);
}
person A_Arnold    schedule 09.08.2019

Оператор instanceof — это простой подход, когда вы не владеете классами. Выражение instanceof истинно, если объект является данным классом или подклассом.

Вы упомянули, что вам не принадлежат классы. Владелец может ввести подклассы в последующем выпуске. Скажем, владелец вводит APlus как подкласс A. Экземпляр APlus — это A. Код, который работает на A, должен работать и на APlus. Если вы используете instanceof, ваш код будет продолжать работать без каких-либо усилий с вашей стороны. Если вы используете имена классов, это приведет к ошибке - без уведомления вашего компилятора.

Если вы неоднократно включаете один и тот же объект, может оказаться полезным один раз обернуть объект в класс-оболочку, реализующий интерфейс. После этого вы можете просто вызывать методы интерфейса — без if, switch или карты.

public interface IWrapper {
    public void handle();
    public String describe();
}

public AWrapper implements IWrapper { ... }
public BWrapper implements IWrapper { ... }
public CWrapper implements IWrapper { ... }
public UnknownWrapper implements IWrapper { ... }

IWrapper wrap( Object o ) {
    if ( o instanceof A ) return new AWrapper((A) o);
    else if ( o instanceof B ) return new BWrapper((B) o);
    else if ( o instanceof C ) return new CWrapper((C) o);
    else return new UnknownWrapper(o);
}

Даже при гарантированном отсутствии подклассов избегайте указания имен классов в виде литеральных строк в switch случаях. Это допускает ошибки, которые компилятор не найдет, что может стоить вам времени на отладку.

person Andy Thomas    schedule 10.04.2015

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

public enum Classes {
  // changed enum constants a bit to avoid confusing with target class names
  ClsA (A.class),
  ClsB (B.class),
  ClsC (C.class),
  UNKNOWN(null);
  private final Class<?> targetClass;
  Classes(Class<?> targetClass) {
    this.targetClass = targetClass;
  }
  public static Classes fromClass(Class<?> cls) {
    for(Classes c : values()) {
      if(c.targetClass == cls)
         return c;
    }
    return UNKNOWN;
  }
}

switch (Classes.fromClass(o.getClass())) {
case ClsA:
  handleA((A)o);
  break;
case ClsB:
  handleB((B)o);
  break;
case ClsC:
  handleC((C)o);
  break;
default:
  handleUnknown(o);
  break;
}

если вы получаете значительное количество известных классов, рассмотрите возможность использования карты вместо итерации в Classes.fromClass, например:

public enum Classes {
  ClsA(A.class),
  ClsB(B.class),
  // etc...
  UNKNWON(null);

  // need a wrapper class to avoid compilation problem
  // with referring static enum field within an initializer 
  private static class Holder {
    public static final IdentityHashMap<Class<?>, Classes> map = new IdentityHashMap<>();
  }
  Classes(Class<?> targetClass) {
    Holder.map.put(targetClass, this);
  }
  public static Classes fromClass(Class<?> cls) {
    Classes c = Holder.map.get(cls);
    return c != null ? c : UNKNOWN;
  }
}
person Ramiz    schedule 22.11.2016

Вот пример этого, который использует простой объект для каждого случая.

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

Мне удалось обойти java.lang.reflect

import java.lang.reflect.Method;

public class MyClass {

    public void validate(Object o) {    
        String className = o.getClass().getSimpleName();     
        try {
            //this line searches a method named as className
            Method m = this.getClass().getDeclaredMethod(className);
            //this line execute the method 
             m.invoke(this);
        } catch (Exception e) {
            e.printStackTrace();
            handleUnknown();
        }

    }

    //this methot will execute if the object o is instance of A
    public void A() {

    }
    //this methot will execute if the object o is instance of B
     public void B() {

    }
    //this methot will execute if the object o is instance of C
     public void C() {

    }
    //this methot will execute if the method is unknown
    public void handleUnknown(){

    }


}
person Edgar    schedule 12.07.2017

Я хочу немного изменить ответ Эдгара

    public void handle(Object value){
        try {

            getClass().getDeclaredMethod("handle", value.getClass()).invoke(this, value);

        } catch (NoSuchMethodException | SecurityException | InstantiationException | IllegalAccessException | IllegalArgumentException | InvocationTargetException e) {
            throw new HandleException(e); // public class HandleException extends RuntimeException {...} 
        }
    }

методы в зависимости от типа:

void handle(A a){}
void handle(B b){}
void handle(C c){}
person Oleksandr Potomkin    schedule 15.02.2021

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

Создайте Enum с именами классов.

public enum ClassNameEnum {
    ClassA, ClassB, ClassC
}

Найдите имя класса объекта. Напишите регистр switch поверх перечисления.

private void switchByClassType(Object obj) {

        ClassNameEnum className = ClassNameEnum.valueOf(obj.getClass().getSimpleName());

        switch (className) {
            case ClassA:
                doA();
                break;
            case ClassB:
                doB();
                break;
            case ClassC:
                doC();
                break;
        }
    }
}
person Siva Kumar    schedule 27.06.2018
comment
Ограничение этого подхода заключается в том, что мы не можем легко найти использование определенного класса с помощью этого переключателя. В IDE обычно есть функция поиска использования класса, а не строки. Для строк мы должны использовать опцию поиска текста. Это может быть громоздко, и мы можем пропустить некоторые варианты использования. - person MasterJoe; 21.10.2019