Вызов метода неизвестного объекта - PullRequest
0 голосов
/ 05 октября 2018

У меня есть два ArrayList - ArrayList1 и ArrayList2.Каждый из них заполнен объектами - Object1 и Object2 соответственно.Оба этих объекта имеют метод 'getText'.

Object1:

public String getText() { return "1";}

Object2:

public String getText() { return "2";}

В определенный момент я хотел бы пройтись по каждому из этихсписки, использующие один и тот же метод (только с другим параметром).

loopThroughList(1)
loopThroughList(2)

Каков синтаксис, если я хочу вызвать метод, но я не знаю, каким объектом это будет?Это код, который у меня пока есть:

for (Object o : lists.getList(listNumber)) {
    System.out.println(o.getText());
}

Там написано Не удается разрешить метод getText .Я погуглил и нашел другое решение:

for (Object o : lists.getList(listNumber)) {
    System.out.println(o.getClass().getMethod("getText"));
}

Но это дает мне NoSuchMethodException ошибку.Хотя метод getText является публичным.

РЕДАКТИРОВАТЬ: Чтобы получить правильный список, я вызываю метод 'getList' другого объекта (списков), который возвращает либо ArrayList1 или ArrayList2 (в зависимости от предоставленного параметра).

class Lists

public getList(list) {
    if (list == 1) {
        return ArrayList1;
    }
    else if (list == 2) {
        return ArrayList2;
    }
}

Ответы [ 6 ]

0 голосов
/ 05 октября 2018

Просто чтобы добавить больше к этому ответу и дать вам еще немного подумать над этим (постараюсь сделать это простым, неформальным способом).Использование интерфейсов является правильным способом выполнения такой операции.Однако я хочу остановиться на «плохой идее»:

for (Object o : lists.getList(listNumber)) {
    System.out.println(o.getClass().getMethod("getText"));
}

То, что вы здесь делаете, использует механизм под названием Отражение :

Отражение - это особенность языка программирования Java.Это позволяет исполняющей Java-программе исследовать или «анализировать» себя и манипулировать внутренними свойствами программы.Например, класс Java может получить имена всех своих членов и отобразить их.

То, что вы на самом деле пытались использовать, использует этот механизм для извлечения метода с помощью Class отраженияэкземпляр объекта вашего класса (звучит странно, не правда ли?).

С этой точки зрения вам нужно подумать, что, если вы хотите вызвать свой метод, у вас теперь есть, в некотором смысле, мета-Класс для управления вашими объектами.Думайте об этом как об объекте, который находится на один шаг выше ваших объектов (аналогично сну во сне, в Начало ).В этом смысле вам нужно извлечь метод, а затем вызвать его другим (мета-подобным) способом:

java.lang.reflect.Method m = o.getClass().getMethod("getText");
m.invoke(o);

Используя эту логику, вы можете перебирать список объектов, проверить, если методсуществует, затем вызовите ваш метод.

Это плохая, ПЛОХАЯ идея.

Почему?Что ж, ответ зависит от самого отражения: отражение напрямую связано с временем выполнения - т.е. когда программа выполняется, практически выполняя все вещи во время выполнения, минуя мир компиляции.

Другими словами, делая это, вы обходите механизм ошибок компиляции Java, позволяя таким ошибкам возникать в runtime .Это может привести к нестабильному поведению программы во время ее выполнения - помимо снижения производительности при использовании Reflection, которое здесь не будет анализироваться.

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

С другой стороны, вы можете следовать механизму наследования Java через классы и интерфейсы - определить интерфейс с помощью вашего метода (давайте назовем это Textable), убедитесь, что ваши классы реализуют его, а затем используйте его в качестве базового объекта в объявлении списка (@alexrolea реализовал это в своем ответе, как и @OldCurmudgeon).

Таким образом, ваша программа по-прежнему будет принимать решение о вызове метода во время выполнения (с помощью механизма, называемого позднее связывание ), но вы не обойдете механизм ошибок компиляции Java.Подумайте об этом: что произойдет, если вы определите реализацию Textable без предоставления класса - ошибка компиляции!А что если вы поместите нетекстовый объект в список Textable с?Угадай, что!Снова ошибка компиляции.И этот список можно продолжить ....

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

ОБНОВЛЕНИЕ: Как показывают некоторые ответы, вы можете использовать instanceof, чтобы проверить, есть ли у вас конкретный экземпляр объекта Class, который содержит ваш метод, а затем вызвать соответственно.Хотя это кажется простым решением, оно плохо с точки зрения масштабирования: что, если у вас есть 1000 различных классов, которые реализуют такой же метод, который вы хотите вызвать?

0 голосов
/ 05 октября 2018

Интерфейсы отлично работают здесь, но есть пара других опций, если вы имеете дело с устаревшим кодом и не можете использовать интерфейсы.

Сначала нужно привести элементы списка к их соответствующим типам:

for (Object o : lists.getList(listNumber)) {
    if(o instanceof Object1) {
        Object1 o1 = (Object1)o;
        System.out.println(o1.getText());
    }
    else if(o instanceof Object2) {
        Object1 o2 = (Object2)o;
        System.out.println(o2.getText());
    }
    else {
        System.out.println("Unknown class");
    }
}

Вы также можете использовать отражение, чтобы увидеть, есть ли у объекта метод getText, а затем вызвать его:

for (Object o : lists.getList(listNumber)) {
    try {
        System.out.println(o.getClass().getDeclaredMethod("getName").invoke(o));
    }
    catch(Exception e) {
        System.out.println("Object doesn't have getText method");
    }
}
0 голосов
/ 05 октября 2018

Это ужасно.Можете ли вы уточнить, что конкретно вы пытаетесь сделать?Java имеет строгий тип дизайна, и вы пытаетесь обойти это.Зачем?Вместо Object используйте определенный класс или интерфейс, как было предложено ранее.Если это невозможно, и вы должны использовать списки объектов, используйте instanceof и приведение, например:

for (Object o : lists.getList(listNumber)) {
    if (o instanceof Object1) {
        Object1 o1 = (Object1) o;
        System.out.println(o1.getText());
    } else if (o instanceof Object2) {
        Object2 o2 = (Object2) o;
        System.out.println(o2.getText());
    }
}
0 голосов
/ 05 октября 2018

Здесь interface s входит.

interface HasText {
    public String getText();
}

class Object1 implements HasText {
    @Override
    public String getText() {
        return "1";
    }
}

class Object2 implements HasText {
    @Override
    public String getText() {
        return "2";
    }
}

private void test() {
    List<HasText> list = Arrays.asList(new Object1(), new Object2());
    for (HasText ht : list) {
        System.out.println(ht);
    }
}

Если один из ваших объектов не находится под вашим контролем, вы можете использовать класс Wrapper.

class Object3DoesNotImplementHasText {
    public String getText() {
        return "3";
    }
}

class Object3Wrapper implements HasText{
    final Object3DoesNotImplementHasText it;

    public Object3Wrapper(Object3DoesNotImplementHasText it) {
        this.it = it;
    }

    @Override
    public String getText() {
        return it.getText();
    }
}

private void test() {
    List<HasText> list = Arrays.asList(new Object1(), new Object2(), new Object3Wrapper(new Object3DoesNotImplementHasText()));
    for (HasText ht : list) {
        System.out.println(ht);
    }
}
0 голосов
/ 05 октября 2018

ваши объекты должны реализовывать общий интерфейс.

interface GetTextable {
    String getText();
}

class One implements GetTextable {
    private final String text;

    public One(final String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }
}

class Two implements GetTextable {
    private final String text;

    public Two(final String text) {
        this.text = text;
    }

    public String getText() {
        return this.text;
    }
}

@Test
public void shouldIterate() throws Exception {
    List<GetTextable> toIterate = Arrays.asList(new One("oneText"), new Two("twoText"));
    for(GetTextable obj: toIterate) {
        System.out.println(obj.getText());
    }
}
0 голосов
/ 05 октября 2018

Определение интерфейса для метода getText

public interface YourInterface {

    String getText();     

}

Реализация интерфейса для соответствующих классов

public class Object1 implements YourInterface {

    @Override
    public String getText() { 
        return "1";
    }

}

public class Object2 implements YourInterface {

    @Override
    public String getText() { 
        return "2";
    }

}

Измените свой метод getList, чтобы он возвращал List<YourInterface>

public static List<YourInterface> getList(int list){
    List<YourInterface> result = new ArrayList<>();
    if(list == 1){
        // your initial type
         List<Object1> firstList = new ArrayList<>();
         result.addAll(firstList);
    } else {
        // your initial type
        List<Object2> secondList = new ArrayList<>();
        result.addAll(secondList);
    }
    return result;
}

Декларация для loopThroughList

public static void loopThroughList(List<YourInterface> list){
    list.forEach(yourInterface -> System.out.println(yourInterface.getText()));
}

Пример использования.

public static void main(String[] args) {
    loopThroughList(getList(1));
    loopThroughList(getList(2));
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...