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

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

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

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

    第 1 条附言  ·  314 天前
    结帖:这个是比较好对方案了:

    @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   315 天前 via Android   ❤️ 1
    可以传递一个 Runnable 对象
    xizismile
        2
    xizismile   315 天前 via Android
    方式 1:用对象来包装逻辑传递给其它方法
    方式 2:用 java8 提供的函数式接口来实现
    xiangyuecn
        3
    xiangyuecn   315 天前
    delegate ? C#打死也不用这么难懂的玩意(主要是这个单词手写敲不出来),还不如定义一个 interface 来的统一实在。。。实际上还是 Action 、Func 随便定义随便用,随便当参数,虽然本质上还是 delegate

    java:interface,统一实在,没参数的用 Runnable 省去了一个 interface 定义
    KentY
        4
    KentY   315 天前
    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   315 天前
    可以传逻辑,例如使用 Function
    xgfan
        6
    xgfan   315 天前
    这不就是传个函数进去吗……
    atfeel
        7
    atfeel   315 天前
    @xiangyuecn interface 不灵活,还要定义,麻烦啊
    MineDog
        8
    MineDog   315 天前 via Android
    lambda 表达式啊,前提是要函数式接口
    luckyrayyy
        9
    luckyrayyy   315 天前
    完全,可能你得自己做一些工作。你可以动态生成一个 class 然后执行,直接执行代码块的话就不太确定行不行了。
    xiangyuecn
        10
    xiangyuecn   315 天前
    @atfeel #7 不会,无参的直接用系统的 Runnable 不用定义。有参的自己写个,大不了参数全为 Object😂

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

    .
    .
    .
    先定义几个全局通用的。要什么参数的、返回值的类里面直接嵌套定义,哪里要就定义到哪里
    ipwx
        11
    ipwx   315 天前
    Java 8 不是有语法糖么,配合 interface 可以 f(x -> x + 1) 之类的
    ipwx
        12
    ipwx   315 天前   ❤️ 1
    不过我记得 java 8 lambda 最大的问题不是有没有语法糖,而是 exception 检查那套。。。
    atfeel
        13
    atfeel   315 天前
    @xiangyuecn 你的方法看起来不错,能随便写个模型我看看吗,分水岭难于逾越
    谢谢
    Febers
        14
    Febers   315 天前
    遇到过同样的需求,在没有改写成 Kotlin 之前,使用的是 Runnable 然后手动调 run ;改写成 Kotlin 之后看起来还挺酷的:
    //定义
    var action: ()->Unit = { }
    //赋值
    action = {
    //some code
    }
    //调用
    action.invoke() //action()
    xiangyuecn
        15
    xiangyuecn   315 天前
    #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   314 天前   ❤️ 2
    Consumer<T>:消费型接口(有钱咱就花)
    接收一个数据,并且处理这个数据,处理完成之后,
    不需要返回任何数据,直接输出
    con.accept();

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

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

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

    至于需要动态逻辑、动态构造代码并执行的,那需要重新来套 lexer, syntaxer, interpreter/compiler 等,而且手动拼装语句容易错,如果是远程给代码直接调用,不如直接用个 JVM 内的解释器。
    或者含泪实践格林斯潘第十定律。
    Febers
        25
    Febers   314 天前
    @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   314 天前   ❤️ 1
    个人觉得 Kotlin 的很多东西很有趣,学习 Kotlin 反编译之后的 Java 代码,基本上就是用 Java 如何实现 Kotlin 特性的工业化答案。
    这个问题很多同学都给了答案,重点在于:Java 是纯面向对象语言,一切方法参数都是对象(基本数据类型不谈),而匿名内部类的存在让我们可以使用 自定义方法的对象,虽然写法看起来比较啰嗦;引入 Lambda 之后的 Java 代码写起来更“函数式”了,但本质上还是使用对象的概念
    Febers
        27
    Febers   314 天前
    如果要深究到语言特性的话,对于这样的需求,Java 字节码层面支持是 invokedynamic 指令的引入,语法层面的支持是 Lambda 的引入
    no1xsyzy
        28
    no1xsyzy   314 天前
    @Febers #25 这个匿名子类的做法,swing 的样例代码里就有……
    Febers
        29
    Febers   314 天前
    @no1xsyzy #28 😮可能因为写起来比较好理解?
    zclHIT
        30
    zclHIT   314 天前
    functional interface
    no1xsyzy
        31
    no1xsyzy   314 天前
    @Febers #29 Java 7 的时候 Callback 的写法只有这样吧…… 我 Java 用得不多,也是老早的事了,GUI 的 handler 就差不多只能这么玩,哪像 Qt 用槽。
    sandrew1945
        32
    sandrew1945   314 天前
    使用 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   314 天前 via Android
    effctive java 3rd 44 优先使用标准的函数式接口
    CoderGeek
        34
    CoderGeek   314 天前
    Functional
    stevenkang
        35
    stevenkang   314 天前
    推荐文章 [Java8 新特性学习-函数式编程]( https://blog.csdn.net/icarusliu/article/details/79495534)
    gmywq0392
        36
    gmywq0392   314 天前
    exec
    atfeel
        37
    atfeel   314 天前
    @Febers 完美,可以了,可以了,可以结贴了,感谢,非常感谢!!
    kingiis
        38
    kingiis   313 天前
    你是想问 动态生成 java 代码 动态执行么
    不如切到 js 动态 js 倒是很容易
    关于   ·   帮助文档   ·   FAQ   ·   API   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   2384 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 25ms · UTC 16:20 · PVG 00:20 · LAX 08:20 · JFK 11:20
    ♥ Do have faith in what you're doing.