Java 能把代码块当参数传递到其他方法并执行吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
atfeel
V2EX    Android

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

  •  
  •   atfeel 2020-04-27 20:34:48 +08:00 15538 次点击
    这是一个创建于 1998 天前的主题,其中的信息可能已经有所发展或是发生改变。
    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
    6IbA2bj5ip3tK49j
        6
    6IbA2bj5ip3tK49j  
       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     Solana     901 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 28ms UTC 20:16 PVG 04:16 LAX 13:16 JFK 16:16
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86