V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
atfeel
V2EX  ›  Android

Java 能把代码块当参数传递到其他方法并执行吗?

  •  
  •   atfeel · 2020-04-27 20:34:48 +08:00 · 7665 次点击
    这是一个创建于 1430 天前的主题,其中的信息可能已经有所发展或是发生改变。
    public void Main(){
    
        //在这里定义代码块,并传入 Test 方法里
        Test(
            delegate(){
                //代码逻辑
            }
        );
    }    
        
    public void  Test(dele a)
    {
        invoke(a);////在这里执行传入的代码块
    }
    

    这种写法类似 C#到委托,java 里能做到吗?

    第 1 条附言  ·  2020-04-28 16:12:27 +08:00
    结帖:这个是比较好对方案了:

    @FunctionalInterface
    public interface Action {
    void invoke();
    }

    public Main()
    {
    Action action;
    action = new Action() {
    @Override
    public void invoke() {
    //code...
    }
    };
    }

    感谢各位 V 有对热心帮助
    38 条回复    2020-04-29 14:10:18 +08:00
    yikuo
        1
    yikuo  
       2020-04-27 20:37:50 +08:00 via Android   ❤️ 1
    可以传递一个 Runnable 对象
    xizismile
        2
    xizismile  
       2020-04-27 20:45:16 +08:00 via Android
    方式 1:用对象来包装逻辑传递给其它方法
    方式 2:用 java8 提供的函数式接口来实现
    xiangyuecn
        3
    xiangyuecn  
       2020-04-27 20:48:04 +08:00
    delegate ? C#打死也不用这么难懂的玩意(主要是这个单词手写敲不出来),还不如定义一个 interface 来的统一实在。。。实际上还是 Action 、Func 随便定义随便用,随便当参数,虽然本质上还是 delegate

    java:interface,统一实在,没参数的用 Runnable 省去了一个 interface 定义
    KentY
        4
    KentY  
       2020-04-27 20:59:30 +08:00
    java 的源代码需要编译才能在 jvm 里执行. 所以你不能传递一个动态的 string, 然后希望 java 执行它.
    java 可以动态读取源代码文件并编译成 class, 比如通过 JavaCompiler 编译, 然后再通过 reflection 执行编译好的 class 里的 method.但是我猜这不是你需要的.
    如果我没理解错的话, 你大概需要一个类似 callback 的东西, 如果 java 版本是 8+, 它提供了一些现成的接口, 也可以就定义一个 classFooCallback {delegate(){...}}, 然后传递 FooCallback fooCallback instead of "dele a" . 当执行的时候, fooCallback.delegate()就可以了.
    按一般来说, 你的 delegate method 需要参数, 否则意义不大.
    chihiro2014
        5
    chihiro2014  
       2020-04-27 21:00:39 +08:00
    可以传逻辑,例如使用 Function
    xgfan
        6
    xgfan  
       2020-04-27 21:02:00 +08:00
    这不就是传个函数进去吗……
    atfeel
        7
    atfeel  
    OP
       2020-04-27 21:11:59 +08:00
    @xiangyuecn interface 不灵活,还要定义,麻烦啊
    MineDog
        8
    MineDog  
       2020-04-27 21:15:28 +08:00 via Android
    lambda 表达式啊,前提是要函数式接口
    luckyrayyy
        9
    luckyrayyy  
       2020-04-27 21:15:42 +08:00
    完全,可能你得自己做一些工作。你可以动态生成一个 class 然后执行,直接执行代码块的话就不太确定行不行了。
    xiangyuecn
        10
    xiangyuecn  
       2020-04-27 21:23:59 +08:00
    @atfeel #7 不会,无参的直接用系统的 Runnable 不用定义。有参的自己写个,大不了参数全为 Object😂

    interface Func<T>{
    void fn(T...args); //Type safety: Potential heap pollution via varargs parameter args😂 这坨带码只是意思意思
    }

    .
    .
    .
    先定义几个全局通用的。要什么参数的、返回值的类里面直接嵌套定义,哪里要就定义到哪里
    ipwx
        11
    ipwx  
       2020-04-27 21:39:26 +08:00
    Java 8 不是有语法糖么,配合 interface 可以 f(x -> x + 1) 之类的
    ipwx
        12
    ipwx  
       2020-04-27 21:39:53 +08:00   ❤️ 1
    不过我记得 java 8 lambda 最大的问题不是有没有语法糖,而是 exception 检查那套。。。
    atfeel
        13
    atfeel  
    OP
       2020-04-27 21:43:16 +08:00
    @xiangyuecn 你的方法看起来不错,能随便写个模型我看看吗,分水岭难于逾越
    谢谢
    Febers
        14
    Febers  
       2020-04-27 21:46:31 +08:00
    遇到过同样的需求,在没有改写成 Kotlin 之前,使用的是 Runnable 然后手动调 run ;改写成 Kotlin 之后看起来还挺酷的:
    //定义
    var action: ()->Unit = { }
    //赋值
    action = {
    //some code
    }
    //调用
    action.invoke() //action()
    xiangyuecn
        15
    xiangyuecn  
       2020-04-27 21:56:19 +08:00
    #13 没格式,自己格式化一下再看

    static class aaa{
    static void exec(Func<Integer, Boolean, String> func1, Runnable action1, Action<String,Object,Integer> action2) {
    String val=func1.call(123, true);
    action1.run();
    action2.call(val, new java.util.Date(), 456);
    }
    interface Action<T1,T2,T3>{
    void call(T1 a1,T2 a2,T3 a3);
    }
    interface Func<T1, T2, ReturnType>{
    ReturnType call(T1 a1,T2 a2);
    }
    static public void test() {
    exec(new Func<Integer, Boolean, String>(){
    @Override public String call(Integer a1, Boolean a2) {
    return a1+" "+a2;
    }
    },new Runnable() {
    @Override public void run() {
    System.out.println("Runnable");
    }
    },new Action<String, Object, Integer>() {
    @Override public void call(String a1, Object a2, Integer a3) {
    System.out.println(a1+" "+a2+" "+a3);
    }
    });
    }
    }


    liyanggyang
        16
    liyanggyang  
       2020-04-28 09:41:19 +08:00   ❤️ 2
    Consumer<T>:消费型接口(有钱咱就花)
    接收一个数据,并且处理这个数据,处理完成之后,
    不需要返回任何数据,直接输出
    con.accept();

    Supplier<T>:供给型接口(后勤)
    提供需要用到的数据
    sup.get();

    Function<T, R>:函数型接口(乾坤大挪移)
    接收 T 类型转换为 R 类型
    fun.apply();

    Predicate<T>:断言型接口(包公断案)
    判断对与错,把控 if 关卡
    lff0305
        17
    lff0305  
       2020-04-28 10:04:17 +08:00
    Java 有很多 Runtime Compiler, 比如 logback 在用的 Janino, 等等
    以前实现类似的功能用的是 JDK8 里带的 js 引擎, js 当字符串传过去 eval 就 OK
    Jrue0011
        18
    Jrue0011  
       2020-04-28 10:19:16 +08:00
    JDK8 以后 java.util.function 下的接口基本能满足大部分日常需求了
    atfeel
        19
    atfeel  
    OP
       2020-04-28 10:28:24 +08:00
    @Febers 我没用 Kotlin,还是 java
    atfeel
        20
    atfeel  
    OP
       2020-04-28 10:30:09 +08:00
    @xiangyuecn 感谢
    atfeel
        21
    atfeel  
    OP
       2020-04-28 10:33:25 +08:00
    @Febers 你的这个方法就是我想要的了
    atfeel
        22
    atfeel  
    OP
       2020-04-28 10:34:45 +08:00
    @Febers Runnable 的用法,能举个例子吗
    gz233
        23
    gz233  
       2020-04-28 10:58:12 +08:00
    @liyanggyang 正解
    no1xsyzy
        24
    no1xsyzy  
       2020-04-28 11:19:12 +08:00
    感觉没什么分水岭的,只要你不是动态逻辑(即执行的代码是编译时决定的),那就可以用对象实现闭包,甚至如果不需要闭包就传个匿名类就行。

    至于需要动态逻辑、动态构造代码并执行的,那需要重新来套 lexer, syntaxer, interpreter/compiler 等,而且手动拼装语句容易错,如果是远程给代码直接调用,不如直接用个 JVM 内的解释器。
    或者含泪实践格林斯潘第十定律。
    Febers
        25
    Febers  
       2020-04-28 12:03:49 +08:00
    @atfeel #22
    Kotlin 代码的本质是,定义一个类型属于 () -> Unit 的变量,然后动态赋值,通过 invoke 方法执行,反编译 Kotlin 的字节码之后可以发现,其对应的 Java 类型是 Function0 接口,位于 kotlin.jvm.functions,仿照其写法

    //使用了 @FunctionalInterface 以使用 Lambda
    @FunctionalInterface
    interface Action {
    void invoke();
    }

    //定义与赋值
    Action action;
    action = () -> {
    //some code 1
    };
    //非 Lambda 写法
    action = new Action() {
    @Override
    public void invoke() {
    //some code 2
    }
    };

    //调用
    action1.invoke();

    使用 Runnable 的原因很简单,它是系统定义的一个 FunctionalInterface,而且很普通,调用 run 方法也跟多线程没有关系,把 Runnable 替换成上面的 Action,就可以少写一个接口
    Febers
        26
    Febers  
       2020-04-28 12:14:40 +08:00   ❤️ 1
    个人觉得 Kotlin 的很多东西很有趣,学习 Kotlin 反编译之后的 Java 代码,基本上就是用 Java 如何实现 Kotlin 特性的工业化答案。
    这个问题很多同学都给了答案,重点在于:Java 是纯面向对象语言,一切方法参数都是对象(基本数据类型不谈),而匿名内部类的存在让我们可以使用 自定义方法的对象,虽然写法看起来比较啰嗦;引入 Lambda 之后的 Java 代码写起来更“函数式”了,但本质上还是使用对象的概念
    Febers
        27
    Febers  
       2020-04-28 12:21:33 +08:00
    如果要深究到语言特性的话,对于这样的需求,Java 字节码层面支持是 invokedynamic 指令的引入,语法层面的支持是 Lambda 的引入
    no1xsyzy
        28
    no1xsyzy  
       2020-04-28 12:53:48 +08:00
    @Febers #25 这个匿名子类的做法,swing 的样例代码里就有……
    Febers
        29
    Febers  
       2020-04-28 12:59:45 +08:00
    @no1xsyzy #28 😮可能因为写起来比较好理解?
    zclHIT
        30
    zclHIT  
       2020-04-28 13:03:38 +08:00
    functional interface
    no1xsyzy
        31
    no1xsyzy  
       2020-04-28 13:11:47 +08:00
    @Febers #29 Java 7 的时候 Callback 的写法只有这样吧…… 我 Java 用得不多,也是老早的事了,GUI 的 handler 就差不多只能这么玩,哪像 Qt 用槽。
    sandrew1945
        32
    sandrew1945  
       2020-04-28 13:21:37 +08:00
    使用 Lambda,参考 vert.x
    ```
    vertx.executeBlocking(this::blockingCode, this::resultHandler);

    private void blockingCode(Promise<JsonObject> promise)
    {
    // do something
    }

    private void resultHandler(AsyncResult<JsonObject> ar)
    {
    // get result
    }
    ```
    其中 executeBlocking 是这样定义的
    ```
    <T> void executeBlocking(Handler<Promise<T>> var1, Handler<AsyncResult<T>> var2);


    public interface Handler<E> {
    void handle(E var1);
    }
    ```
    aguesuka
        33
    aguesuka  
       2020-04-28 13:50:44 +08:00 via Android
    effctive java 3rd 44 优先使用标准的函数式接口
    CoderGeek
        34
    CoderGeek  
       2020-04-28 14:45:43 +08:00
    Functional
    stevenkang
        35
    stevenkang  
       2020-04-28 14:48:12 +08:00
    推荐文章 [Java8 新特性学习-函数式编程]( https://blog.csdn.net/icarusliu/article/details/79495534)
    gmywq0392
        36
    gmywq0392  
       2020-04-28 14:59:26 +08:00
    exec
    atfeel
        37
    atfeel  
    OP
       2020-04-28 16:08:34 +08:00
    @Febers 完美,可以了,可以了,可以结贴了,感谢,非常感谢!!
    kingiis
        38
    kingiis  
       2020-04-29 14:10:18 +08:00
    你是想问 动态生成 java 代码 动态执行么
    不如切到 js 动态 js 倒是很容易
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   3951 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 10:22 · PVG 18:22 · LAX 03:22 · JFK 06:22
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.