LINUX.ORG.RU

Как написать хеш с параметрами и функциями на яве?

 


0

1

Упоротость по js не проходит бесследно. Мне надо на яве сляпать приёмник команд из amqp. Команды имеют вид «<имя> <параметр 0> <...>». То есть я просто бедру строку из amqp, потом делаю split по пробелу, по первому элементу выбираю команду, остальное все скидываю в массив аргументов. Если он короче, чем может взять моя функция, то выплёвываем ошибку, если нет, вызываем функцию и выплёвываем то, что она вернёт.
Вроде всё просто и на js я бы это быстро накидал, но тут я что-то затупил. Во-первых стрелки (которые лямблы) работают на какой-то магии:

interface TwoArgInterface {
    public int operation(int a, int b);
}

public class MyClass {

    public static void main(String javalatte[]) {
        TwoArgInterface plusOperation = (a, b) -> a + b;
        System.out.println("Sum of 10,34 : " + plusOperation.operation(10, 34));
    }
}
TwoArgInterface plusOperation = (a, b) -> a + b;Что это значит??
Как он узнаёт, что лямбду надо прицепить к int operation??
Что будет, если в интерфейсе больше двух методов?
Как туда запихать больше двух методов?
Но это ладно. Тут в принципе возможно сделать метод, который принимает функцию, чтобы нахерачить что-то в виде:
//js
commands : {
    setLogQueue : {
        argc : 1,
        run : (arg) -> {main.setLogQueue(arg); return "Ok";}
    }
...
}

...
//cmd - command string from message, agrs - array from message.
command = commands[cmd]
if (command) {
    if (command.argc < args.length) return "I need MORE args";
    return command.run(...args);
} else {
    return "Command "+cmd+" not found.";
}
??

Пока писал пост допёрло. Сделал так:

class xxx {
class Command {
        public Command(int pargc, Function<String,String> prun) {
            this.argc = pargc;
            this.run = prun;
        }
        public int argc;
        public Function<String, String> run;
    }

    private Command cmd(int pargc, Function<String, String> prun) {
        return new Command(pargc, prun);
    }
    ...
    commands.put("setLogQueue", cmd(1, (arg)->
            {
                app.setLogQueneName(arg); 
                return arg;
            } ));
    ...
}

crutch_master ★★★★★
() автор топика
Ответ на: комментарий от orm-i-auga

Не знаю, что такое amqp

RabbitMQ
Вопрос не как распарсить, а как не писать портянку из switch/case с проверкой количества (и в, перспективе, качества) аргументов.

crutch_master ★★★★★
() автор топика
Последнее исправление: crutch_master (всего исправлений: 1)

Остаётся только вопрос, как сделать метод с переменным числом агрументов и вызвать его с массивом в качестве этих агрументов (подозреваю, что никак).

crutch_master ★★★★★
() автор топика
Ответ на: комментарий от vertexua

Ок, теперь наоборот. Я принимаю 2 агрумента и передаю массив из 2+ агрументов.

void run(String a, String b) {
    println("arg[0]="+a);
    println("arg[1]="+b);
}
...
String[] args = {"arg a","arg b","arg c"};
run(args);
//arg[0]=arg a
//arg[1]=arg b
А, ну и run - это Function<String, String>
Короче, я понял, надо передавать String[]

crutch_master ★★★★★
() автор топика
Последнее исправление: crutch_master (всего исправлений: 1)
Ответ на: комментарий от crutch_master

Если не использовать Reflection, то надо просто написать run(args[0], args[1]). Какого-то синтаксиса нету.

Если через Reflection, то конечно, что угодно можно. Когда получишь экземпляр класса Method, то там invoke как раз массив принимает

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

просто написать run(args[0], args[1])

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

Если через Reflection

Не, это уже слишком сложна. Лучше массив суну, а там буду извращаться с args[0] ...

crutch_master ★★★★★
() автор топика
Ответ на: комментарий от crutch_master

Не, это уже слишком сложна.

Не очень

import java.lang.reflect.*;

public class HelloWorld
{
  public static void main(String[] args) throws Exception
  {
    Method m = OtherClass.class.getMethod("run", float.class, int.class, String.class);

    OtherClass myObject = new OtherClass();
    m.invoke(myObject, new Object[]{1.0f, 10, "hello" });
  }
}

public class OtherClass
{
  public void run(float x, int y, String z)
  {
    System.out.format("%f, %d, %s\n", x, y, z);
  }
}
vertexua ★★★★★
()

Лямбды в java это syntax sugar над анонимными классами, до лямбд (>= java7) было так:

public class MyClass {

    public static void main(String javalatte[]) {
        TwoArgInterface plusOperation = new TwoArgInterface() {
            public int operation(int a, int b) {
                return a + b;
            }
        } // после лямбд: (a, b) -> a + b
        System.out.println("Sum of 10,34 : " + plusOperation.operation(10, 34));
    }
}

Как он узнаёт, что лямбду надо прицепить к int operation??

В коде явно тип указан к которому лямбда присваивается, работает вывод типа в момент компиляции: лямбда=TwoArgInterface.operation(int, int).

Что будет, если в интерфейсе больше двух методов?

Два и более метода? Тогда будет нельзя присвоить лямбду. Так-как в интерфейсе все методы должны быть реализованные, исключение - дефолтные методы (модификатор default), они уже содержат имплементацию и их переопределение опционально.

anonymous
()

Упоротость по js не проходит бесследно. Мне надо на яве сляпать приёмник команд из amqp. Команды имеют вид «<имя> <параметр 0> <...>». То есть я просто бедру строку из amqp, потом делаю split по пробелу, по первому элементу выбираю команду, остальное все скидываю в массив аргументов. Если он короче, чем может взять моя функция, то выплёвываем ошибку, если нет, вызываем функцию и выплёвываем то, что она вернёт.

Тебе нужен reflection.

Как он узнаёт, что лямбду надо прицепить к int operation??

Потому, что там один метод.

Что будет, если в интерфейсе больше двух методов?

Оно не скомпилируется.

Как туда запихать больше двух методов?

Использовать полноценные классы вместо лямбд.

Твой код

TwoArgInterface plusOperation = (a, b) -> a + b;
условно эквивалентен коду
TwoArgInterface plusOperation = new TwoArgInterface() {
  @Override
  public int operation(int a, int b) {
    return a + b;
  }
}
можешь считать это синтаксическим сахаром. Строго говоря это не совсем так, но в данном случае значения не имеет. Если тебе не хватает этого сахара, используй полный синтаксис.

Legioner ★★★★★
()
Последнее исправление: Legioner (всего исправлений: 1)
Ответ на: комментарий от crutch_master

Не слушай наркоманов, одной из идей при реализации было сделать сахаром над анонимными классами, потом отказались

maloi ★★★★★
()

Что будет, если в интерфейсе больше двух методов

Ошибка компиляции

maloi ★★★★★
()
Ответ на: комментарий от maloi

Надо вызвать так: class.method(str); //str is Strnig[]
Вот это: public void run(String a, String b) {...} Число агрументов может отличаться.

crutch_master ★★★★★
() автор топика
Ответ на: комментарий от Legioner

Если тебе не хватает этого сахара, используй полный синтаксис.

Мне надо наделать кучу методов с некоторыми параметрами (типа числа агрументов и может быть еще чего-нибудь) и без switch/case/if-then-else ветвления. Написал как это делается на js, надо чтобы было еще короче:)

crutch_master ★★★★★
() автор топика
Ответ на: комментарий от maloi

и чем-же становится лямбда в жабке при её передаче в хрен знает куда, а не при использовании сразу по месту объявления?

yyk ★★★★★
()
Ответ на: комментарий от vertexua

Не очень

Ну да, с другой стороны, можно нагородить перебиралку методов, а там смореть, какие у них параметры. Думаю - самый красивый вариант.

crutch_master ★★★★★
() автор топика
Ответ на: комментарий от maloi

Смотря какую задачу ты хочешь решить

Это парсер внешних команд, которые я сюда хочу просто и непринуждённо допихивать по мере надобности.

crutch_master ★★★★★
() автор топика
Ответ на: комментарий от maloi

можно создавать объекты без классовой принадлежности? и в байткоде это будет видно? И тип параметра, принимающего лямбду, будет безклассовым? Мы всё ещё про jvm, или сахар кому-то совсем глаза залепил?

yyk ★★★★★
()
Ответ на: комментарий от crutch_master

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

maloi ★★★★★
()
Ответ на: комментарий от maloi

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

crutch_master ★★★★★
() автор топика
Последнее исправление: crutch_master (всего исправлений: 1)

Короче, я сдедал через Reflection и, в принципе, доволен. У меня класс с командами, куда я всё пишу и вот эта херня их ищет

    @Autowired
    private Commands commands;

    private HashMap<String, Method> commandsList;

    public MksControl() {
        commandsList = new HashMap<String, Method>();
        Method[] ms = Commands.class.getMethods();
        for (Method m : ms) commandsList.put(m.getName(), m);
    }

    public String control(String msg) {
        String[] cmd = msg.split(" ");

        String commandStr = cmd[0];
        String[] args = Arrays.copyOfRange(cmd, 1, cmd.length);

        Method m = commandsList.get(commandStr);
        if (m == null) return "Command not found:"+commandStr;

        int c = m.getParameterCount();
        if (c <= args.length) {
            String ret = "Error";
            try {
                args = Arrays.copyOfRange(args, 0, c);
                ret = (String)m.invoke(commands, (Object[])args);
            } catch (Exception e) {
                return "Error when calling command:"+e.getMessage();
            }
            return ret;
        }
        return "Need more args for "+commandStr+"("+c+" vs "+args.length+")";
    }
В rmq я отправляю название метода и агрументы через пробел. Надо теперь сделать как-то, чтобы Commands могло быть любое.

crutch_master ★★★★★
() автор топика
Последнее исправление: crutch_master (всего исправлений: 1)
Ответ на: комментарий от yyk

Отправляйтесь читать спецификацию, естественно с классом, но не анонимным. И да, в байткоде нет объектов, и объекты про байткод ничего не знают.

maloi ★★★★★
()
Ответ на: комментарий от crutch_master

Если тебе прямо перебирать методы - то тоже reflection поможет, через те же методы класса Class. Еще можно написать свою аннотацию, вешать ее на избранные методы. Тогда можно только их регистрировать в твоей системе обработки комманд и даже добавлять параметры.

@Command(name="PING")
void pingCommand(String msg) {
....
}

После процессинга например можно методы хранить в HashMap

Map<String, Method> commands = ...;
Плюс можно хранить вместе с методом и инстанс класса

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 2)
Ответ на: комментарий от crutch_master

Тебе нужен рефлекшн. Разбиваешь строку по пробелам (или как там у тебя), достаёшь из объекта метод с нужным именем и нужным числом аргументов нужного типа и вызываешь его.

Legioner ★★★★★
()
Ответ на: комментарий от crutch_master

Не заметил этот пост, написал ниже. Ну ты как раз так и сделал. Можно еще аннотации юзать чтобы только определенные методы открывать как команды - это правильнее с точки зрения как минимум безопасности. Да и компактности списка методов

Тогда у Method можно вызывать getAnnotation и проверять не null ли вернулся. Лишь если не null, тогда добавлять в мапу

vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 3)
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.