【问题标题】:Passing parameters to public void run() from the parent从父级将参数传递给 public void run()
【发布时间】:2017-08-24 11:39:21
【问题描述】:

我有 50% 的绝望:P。我尝试(一开始)从线程代码更新 GUI,并得到 NullException。看了一段时间后,我了解到 Thread 淹没了 JavaFX 应用程序窗口,为了让我从 Thread 更新 GUI,我需要使用以下代码:

Platform.runLater(new Runnable() {
            @Override public void run() {

            //Update UI here     
            for (int i=0; i<2000;i++)
                {
                 MyMainClass.leftPaneTextArea.appendText("Goodi!\n");
                }

            }
        });

所以我确实使用了这段代码,它确实允许我编辑我的主类的 GUI。但是,我的问题很简单。 =>这个代码,我从一个线程运行,是一个位于公共 void run() 内的代码(我运行代码的线程)。它看起来像这样:

@Override
    public void run() {

    String tmpVar;

    ... (Some code)
    ... (Some code)
    ... (Some code)

        Platform.runLater(new Runnable() {
            @Override public void run() {

            //Update UI here     
            for (int i=0; i<2000;i++)
                {
                 MyMainClass.leftPaneTextArea.appendText("Goodi!\n");
                }

            }
        });

    ... (Some code)
    ... (Some code)
    ... (Some code)
}

如何将参数从父级的 public void run() 传递到这个? 比如String tmpString(我想传下去)。

请注意,如果我在公共 void run() 之外将其声明为静态(例如)=> 我运行了很多线程,因此更新静态 var 可能并不总是准确的(因为很多线程会更新它同时性)

谢谢!

【问题讨论】:

  • Runnable 的 void run() 不接受任何参数,也不返回任何内容。您想要传递的值可以作为字段存储在父类中,然后在子类中调用父类的字段。
  • 只要tmpVar 是最终的(或实际上是最终的,这意味着您只需为其分配一次值),您就可以在“内部”run 方法中访问它。你能edit这个问题让你尝试过的代码更明确一点吗?
  • 如果你制作tmpVar final,你可以在你的匿名Runnable中使用它。

标签: java javafx runnable java-threads


【解决方案1】:

你总是可以使用吸气剂...

public class YourClass {
    private String myVar;

    public void do something(String aVal) {
        myVar= "Value you want to pass";

        this.runOnUiThread(new Runnable() {
            @Override
            public void run() {    
                Log.d("TAG",getMyVar());
            }
        });
    }

    private String myVar(){
        return myVar;
    }
}

【讨论】:

    【解决方案2】:

    基本上,您需要考虑将参数传递给 Runnable 对象,而不是 run() 方法。

    如果你将匿名类设为内部类,那就很清楚了:

    @Override
    public void run() {
    
        String tmpVar;
    
        // ... (Some code)
        // ... (Some code)
        // ... (Some code)
    
            Platform.runLater(new Updater(tmpVar));
    
        // ... (Some code)
        // ... (Some code)
        // ... (Some code)
    }
    
    // ...
    
    public static class Updater implements Runnable {
    
        private final String var ;
    
        public Updater(String var) {
            this.var = var ;
        }
    
    
    
        @Override 
        public void run() {
    
            // Access var here
    
            for (int i=0; i<2000;i++){
                MyMainClass.leftPaneTextArea.appendText("Goodi!\n");
            }
    
        }
    }
    

    现在,如果tmpVarfinal,或者“实际上是final1”,那么它将与您的匿名内部类一起使用,并且基本上被翻译成与内部完全相同的东西上面的类(换句话说,匿名内部类得到一个隐式字段,该字段填充了最终变量的值):

    @Override
    public void run() {
    
        final String tmpVar = ...;
    
        // ... (Some code)
        // ... (Some code)
        // ... (Some code)
    
        Platform.runLater(new Runnable() {
            @Override public void run() {
    
            // access tmpVar here:
    
            for (int i=0; i<2000;i++)
                {
                 MyMainClass.leftPaneTextArea.appendText("Goodi!\n");
                }
    
            }
        });
    
        // ... (Some code)
        // ... (Some code)
        // ... (Some code)
    }
    

    语言设计者本可以使用非最终变量来完成这项工作,但他们认为结果会过于混乱。将会发生的情况是它会被转换为上面看到的相同内部类:换句话说,tmpVarcurrent 值将被隐式传递给匿名内部类中的字段。这将是一个全新的变量,具有与您正在访问的变量不同的范围,并且它的值将是创建匿名内部类时tmpVar 值的“快照”。让一个变量看起来像一个变量,但实际上引用了两个不同的变量可能具有不同的值,这被认为太混乱且容易出错。

    但是,如果tmpVar 不是最终的(或实际上是最终的):即您要多次为其赋值,您可以显式“快照”变量的值:

    @Override
    public void run() {
    
        String tmpVar ;
    
        // ... (Some code)
        // ... (Some code)
        // ... (Some code)
    
        final String varCopy = tmpVar ;
    
        Platform.runLater(new Runnable() {
            @Override public void run() {
    
            // access varCopy here:
    
            for (int i=0; i<2000;i++)
                {
                 MyMainClass.leftPaneTextArea.appendText("Goodi!\n");
                }
    
            }
        });
    
        // ... (Some code)
        // ... (Some code)
        // ... (Some code)
    }
    

    (1) “Effectively final”意味着变量被赋值一次。等效地,这意味着您可以将其声明为 final 而不会产生任何编译错误。

    【讨论】:

    • 将其声明为 final 并将其传递给 run() 的解决方案就像一个魅力。非常感谢!
    【解决方案3】:

    James_D 发布的一个选项是在内部类中使用 final var(匿名与否)

    其他选项,例如new MyRunnable(someString) 或使用Consumer&lt;T&gt;,您可以找到here

    【讨论】:

    • 你不能将Consumer&lt;T&gt; 传递给Platform.runLater(...)
    【解决方案4】:

    你试图做的是反对压倒一切的规则。 如果您要在 run() 方法中传递任何参数,您实际上是在重载 run() 方法,并且无论如何它都不会被调用为 创建新线程时,会调用无参数 run() 方法。

    我建议将信息存储在 static 上下文中的其他位置,然后在 run() 方法中检索它。

    【讨论】:

    • 重载?你确定吗?
    • 如果你创建一个public void run(String ob){...},那么它就是一个超载,对吧?
    • 我不认为 OP 建议向 run() 方法添加参数。由于 OP 在问题本身中指出的原因,创建 static 变量是一个糟糕的主意。
    • 我不是说制作静态变量,而是以某种方式将信息存储在静态“上下文”中。
    • 那会有什么不同?
    猜你喜欢
    • 2020-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-13
    • 1970-01-01
    • 2020-09-22
    • 2019-12-04
    相关资源
    最近更新 更多