Java: как попроще сделать вызов метода по имени?

0000

Допустим есть класс Object

class Object
{
String name;
void OnClick;
}

и два объекта этого класса, напр. A и B с именами "A" и "B".
В зависимости от имени хочется выполнять различные команды (зависимость имя - набор команд один к одному).
Вижу несколько вариантов
1. Прописать всё в OnClick через switch

switch name of
"A": functionA; break;
"B": functionB; break;
...

Получится огромный switch (объектов достаточно много и действия различны, т.е. обычное наследование не катит).
2. Через Reflection вызвать метод "function" + Object.name по имени
Как еще можно? Желательно как можно меньше кода. На правильность/общность/поддерживаемость кода наплевать (пишу для себя).
В идеале хотелось бы все functionA, functionB в отдельном классе. Видимо как статичные методы.
P. S. Время доступа так же не сильно важно, поскольку события вызываются относительно редко.

t1h0n0ff

Правильно ли я понима, что объект класса A = new Object ? И вызов будет в зависимости от имени переменной А?

0000

Ммм, не совсем понял, что имелось виду.
Примерно такого хочется

Object A = new Object;
A.onClick; // ничего или по умолчанию
A.name = "A";
A.onClick; // выполнится functionA

Устроит и static OnClick (String objName). Т.е. по сути задача вызвать функцию по её имени.

Toma90

enum Enum {

A {

@Override
public void onClick {
// ...
}
};

public abstract void onClick;

public static void onClick(String objName) {
valueOf(objName).onClick;
}

}

?

0000

Спасибо, кажется оно! Сейчас проверю :)
P.S. То, что надо.

0000

Только как быть, если в Enum objName не нашелся? Только отлавливать IllegalArgumentException?
Внутри Enum.onClick видимо не отловить :(

Toma90

Например так
 enum Enum {

A {

@Override
public void onClick {
// ...
}
};

private static final java.util.HashMap<String, Enum> map = new java.util.HashMap<>

static {
for (Enum e : values {
map.put(e.name e);
}
}

public abstract void onClick;

public static void onClick(String objName) {
Enum e = map.get(objName);
if (e == null) {
throw new RuntimeException("unexpected name");
}
e.onClick;
}

}

0000

, у меня вызов Enum.onClick("Несуществующий") вызывает сразу NullPointerException без выполнения кода самого статического метода OnClick.
Хотя OnClick вообще пустой поставил, т.е. обращение к элементам списка нет.
Можно конечно вызов Enum.OnClick(obj) снаружи (в классе Object) обернуть проверкой наличия objName в Enum, и если есть, то вызвать.
В принципе пойдет и так...

Блин, у меня в коде косяк был.

agaaaa

Задам типичный для таких случаев вопрос - а зачем?
Вообще, можно сделать HashMap или бор <String, ISingleMethod>

katrin2201

Дизайн кривой. Для таких вещей полиморфизм придуман был. Правильно иметь два разных подкласса твоего обджекта.
Если разные классы создавать не получается, то в зависимости от имени в конструкторе\онклике слукапить один раз нужный комманд, положить в поле, и потом его все время вызывать.
Енум, в прниципе это и делает, только лукап каждый раз происходит, и енумы все-таки не для этого предназначены. Тот, кто это увидит, скажет - втф?

6yrop

то в зависимости от имени в конструкторе\онклике слукапить один раз нужный комманд
откуда слукапить? Все-равно ты где-то пропишешь соответствие [строка Name]->[инстанс полиморфного типа]. Топикстартер, собственно, и спрашивал как это соответствие прописать switch-ем в лоб, заполнение HashMap-а, рефлекшеном или еще как? Енам позволяет получить это соответствие нахаляву, поэтому вариант с енамом лучший.
 
Дизайн кривой.

Если [строка Name] приходит как строка во внешнем параметре, то нормальный дизайн. Если это "магические" строки только в коде, то, да, кривой.

0000

Дизайн кривой. Для таких вещей полиморфизм придуман был
Каждый объект (более 50) в системе имеет свой уникальный обработчик (пишу нестандартную игру). Зачем плодить 100500 классов? Фен-шуй мне не нужен, а вот минимальное количество кода и удобство правки - очень даже.
Вариантом было прилепить скриптовый язык какой, но это лишний оверхеад.
Через Enum весьма неплохой вариант получился. Таким образом всю логику реакции на события вынес в отдельный файл. Меня устраивает.
, добавление методов в данной реализации мне не понравилось. В случае с Enum просто добавляю объект, напр. B в Enum, и прописываю ему нужные методы.

6yrop

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

Dasar

Таким образом всю логику реакции на события вынес в отдельный файл. Меня устраивает.
и чем это стало удобнее?
кусочек такого файла можно посмотреть?

0000

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

kokoc88

Зачем плодить 100500 классов?
Но в варианте ты именно этим и занимаешься.
минимальное количество кода и удобство правки ... Таким образом всю логику реакции на события вынес в отдельный файл.
Это вообще взаимоисключающие параграфы. Один файл, в котором описаны все 50 обработчиков, да ещё и со своими данными, не может быть удобным.
пишу нестандартную игру

Enum.valueOf может за O(N) работать, да ещё и строки всё время сравнивает, для игры такой метод может не подойти по производительности.
В твоём случае вообще не понятно, зачем использовать строковые имена. Почему вместо new GameObject("A") сразу не написать new GameObject(new BehaviorA или new GameObject(Behaviors.A)?

kokoc88

Фен-шуй мне не нужен, а вот минимальное количество кода и удобство правки - очень даже.
Мне не требуется расширяемый-поддерживаемый-удобо-читаемый-код.
:confused:

0000

Но в варианте ты именно этим и занимаешься.
Надо было написать вместо кучи классов - кучу файлов, чтобы смысл был понятен?
Это вообще взаимоисключающие параграфы. Один файл, в котором описаны все 50 обработчиков, да ещё и со своими данными, не может быть удобным.
Тебе виднее :D
Enum.valueOf может за O(N) работать, да ещё и строки всё время сравнивает, для игры такой метод может не подойти по производительности.
Возникновение событий редкое явление и будет вызов через 5мс или 200мс не принципиально.
В твоём случае вообще не понятно, зачем использовать строковые имена. Почему вместо new GameObject("A") сразу не написать new GameObject(new BehaviorA или new GameObject(Behaviors.A)?
Много загадочных вещей можно увидеть в коде, если автор плохо знает язык. И чего теперь сидеть читать литературу по Java, пока желание что-либо делать не пропадет, либо пытаться сделать что-то? Код никто кроме меня не увидит, так что не все ли равно как будет написано?

kokoc88

Надо было написать вместо кучи классов - кучу файлов, чтобы смысл был понятен?
Нет, смысл не стал бы понятен, потому что кучу классов можно написать в одном файле.
Много загадочных вещей можно увидеть в коде, если автор плохо знает язык. И чего теперь сидеть читать литературу по Java, пока желание что-либо делать не пропадет, либо пытаться сделать что-то? Код никто кроме меня не увидит, так что не все ли равно как будет написано?
Желание что-либо делать частенько пропадает, когда твои руки запутались в жирном спагетти. Я задал вполне уместный вопрос, почему бы тебе не сделать new GameObject... Это могло бы облегчить задачу, разве ты не за этим сюда написал?

0000

Объекты будут грузиться из XML-файла и для удобства id-объекта строка. Поэтому мне видится наиболее логичным связывать строку-id и функцию.
Предпочитаю разгребать код когда требуется, а не оптимизировать заранее пока проблемы нет.
Использую уже готовый движок libgdx, так что почти кода как такового будет мало - в основном это будет описание вызовов событий.

kokoc88

Объекты будут грузиться из XML-файла и для удобства id-объекта строка. Поэтому мне видится наиболее логичным связывать строку-id и функцию.
Зачем это делать в рантайме? Наиболее логично связать строку с именем класса, который реализует поведение.
Предпочитаю разгребать код когда требуется, а не оптимизировать заранее пока проблемы нет.
Никто не говорил про оптимизацию, речь идёт о том, как наиболее просто и быстро решить задачу.

0000

Зачем это делать в рантайме? Наиболее логично связать строку с именем класса, который реализует поведение.
Лично мне удобнее хранить объекты именно в XML, чем в Java-коде.
В данном случае возможна привязка не в рантайме?

kokoc88

В данном случае возможна привязка не в рантайме?
Я не ясно выразился. Речь шла о том, что связать строку и объект-поведение можно один раз.

0000

Я правильно понимаю, что в результате должно получится нечто такое:
1. Определяется интерфейс ICommand
2. Создаются классы, реализующие ICommand, и содержащие логику реакций на события.
3. При создании объекта неким образом (каким?) определяем, что вот этому объекту надо сопоставить вот этот метод.
4. В работе программы вызывать объект.Command
?

kokoc88

2. Создаются классы, реализующие ICommand, и содержащие логику реакций на события.
Не обязательно, но можно и так. Всё зависит от конкретной задачи.
3. При создании объекта неким образом (каким?) определяем, что вот этому объекту надо сопоставить вот этот метод.
Как угодно, например, полное имя класса через Reflection.

0000

Если бы я мог это сделать, то данной темы не было бы.
Зачем давать советы, которыми заведомо нельзя воспользоваться? Потешить самомнение что ли?

kokoc88

Зачем давать советы, которыми заведомо нельзя воспользоваться? Потешить самомнение что ли?
Я не понял, каким именно из советов нельзя воспользоваться?

0000

Не обязательно, но можно и так. Всё зависит от конкретной задачи.
Как угодно, например, полное имя класса через Reflection.
На данный момент я делаю вообще чтобы хоть как-нибудь работало, а не выбираю - сделать через интерфейс или по другому.
Про Reflection просто знаю, что есть такое, а вот пользоваться не доводилось.
Разбираться с этим, чтобы выиграть сэкономить несколько мс, зачем? Решение задачи получил и оно уже у меня работает.

agaaaa

Я вообще не знаю, чего ты хочешь, но подозреваю, что XML тебе не нужен. Если, конечно, тебе не надо уметь сущности в игре настраивать через него руками. Но даже в этом случае не совсем понятно.
В общем, я согласен с Майком. Вот, например, как я делаю ачивки в своём порте RoboZZle на андроид:
Есть абстрактный класс ачивок с парой методов - isDone и getRelatedPuzzles, а также полями с именем и иконкой.
Дальше по классу-наследнику на ачивку. Некоторые ачивки наследуют от вспомогательных классов.
Список всех ачивок хранится в коде в виде массива, который заполняется в статическом конструкторе класса Achievements.
Ну и всё. Никаких свитчей и словарей. Экран ачивок просто показывает элементы списка. И при решении паззла пересчитывается состояние всех ачивок.

0000

Если, конечно, тебе не надо уметь сущности в игре настраивать через него руками.
Именно руками и настраиваю. В коде указывать кто, где стоит, какие атрибуты имеет - не хочу.
я делаю ачивки в своём порте
В том вся и загвоздка, что обработчики уникальные полностью, т.е. по клику на один предмет будет выход из игры, по клику на другой проигрываться монолог, а по клику по третьему на сцену добавляться новый предмет. Одинаковых не будет, ибо все предметы уникальны (игра экспериментальная).

katrin2201

Зачем плодить 100500 классов?
Enum это и есть класс. Непонятно, что ты пытаешься сэкономить.
500 классов можно в одном файле тоже задать, если так уж хочется.

katrin2201

Разбираться с этим, чтобы выиграть сэкономить несколько мс, зачем? Решение задачи получил и оно уже у меня работает.
Ты спросил - тебе ответили. Если тебе сойдет и так - то окей, дело твое.

katrin2201

Если бы я мог это сделать, то данной темы не было бы.
для удобства id-объекта строка
Для удобства добавить в xml еще одно поле с именем класса не?

0000

Для удобства добавить в xml еще одно поле с именем класса не?
Не.
Это троллинг какой что ли? Очевидно же, что знаю Яву весьма поверхностно, иначе этого вопроса и не было бы.
Зачем предлагать мне то, что я не смогу реализовать или что потребует дополнительного изучения?
Я ленивый и, если уже найден работающий код, то зачем что-то еще учить, если можно дальше продолжить писать код?
Тем более, что место не критичное.

katrin2201

Ладно, я понял, написал и ладно... Удачи!

apl13

Правильно иметь два разных подкласса твоего обджекта.
Я бы в контексте треда спросил, как джавоверно поступать, если заранее неизвестно, чему равно два, а станет известно только в рантайме. :o

apl13

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

kokoc88

Вариантом было бы перейти с джавы на язык, минимизирующий количество кода.
Неверно, в данном случае вариантом было бы перейти с libgdx на Unity.

katrin2201

Всмысле в рантайме? Код тоже возьмется в рантайме?

apl13

Не код. Процедура.

fufa58

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

katrin2201

Да нет, почему сразу плохое. Просто так как в джаве процедуры не first-level citizen'ы, то там не то что бы много вариантов, куда можно засунуть сгенеренный байткод.

katrin2201

Если до рантайма ты не знаешь, какие процедуры ты будешь вызывать, то у тебя не то, что бы много вариантов.
Есть рефлекшен \ Fast* (CGLIB который жрет сотни-десятки us.
Хочется быстрее - компилируй код.
Но ты это все и сам, наверное, знаешь. К чему вопрос то?

apl13

К чему вопрос то?
Ну к тому, что интересно, как ни странно.
Как в плюсах сделать динамические свойства и методы, я примерно представляю.
Думал, есть ли в джаве распространенные techniques.

psm-home

Думал, есть ли в джаве распространенные techniques.
Есть. Взять groovy.

katrin2201

Есть довольно низкоуровневый ASM. Поверх него можно писать свои какие-то штуки. В основном используется для каких-нибудь хаков, типа генерации хитрых проксей, моков и так далее.
У джавы есть кривоватенький апи для доступа к javac в рантайме.
Можно и упомянутые выше груви/биншеллы юзать, но там свои проблемы.
Оставить комментарий
Имя или ник:
Комментарий: