【问题标题】:Is there a way to take an argument in a callable method?有没有办法在可调用方法中接受参数?
【发布时间】:2012-04-03 12:11:53
【问题描述】:

我创建了一段代码,它获取一个 IP 地址(来自另一个类的 main 方法),然后循环遍历一系列 IP 地址,并在每个 IP 地址进行 ping 操作。我有一个 GUI 前端,它崩溃了(因此我做了多线程。我的问题是我不能再将 IP 地址作为我的 ping 代码中的参数作为它的可调用对象。我已经搜索过为此,似乎无法找到解决此问题的方法。有没有办法让可调用的方法接受参数?如果没有,还有其他方法可以完成我想要做的事情吗?

我的代码示例:

public class doPing implements Callable<String>{

public String call() throws Exception{

    String pingOutput = null;

    //gets IP address and places into new IP object
    InetAddress IPAddress = InetAddress.getByName(IPtoPing);
    //finds if IP is reachable or not. a timeout timer of 3000 milliseconds is set.
    //Results can vary depending on permissions so cmd method of doing this has also been added as backup
    boolean reachable = IPAddress.isReachable(1400);

    if (reachable){
          pingOutput = IPtoPing + " is reachable.\n";
    }else{
        //runs ping command once on the IP address in CMD
        Process ping = Runtime.getRuntime().exec("ping " + IPtoPing + " -n 1 -w 300");
        //reads input from command line
        BufferedReader in = new BufferedReader(new InputStreamReader(ping.getInputStream()));
        String line;
        int lineCount = 0;
        while ((line = in.readLine()) != null) {
            //increase line count to find part of command prompt output that we want
            lineCount++;
            //when line count is 3 print result
            if (lineCount == 3){
                pingOutput = "Ping to " + IPtoPing + ": " + line + "\n";
            }
        }
    }
    return pingOutput;
}
}

IPtoPing 曾经是采用的参数。

【问题讨论】:

    标签: java multithreading arguments ping callable


    【解决方案1】:

    您不能将它作为参数传递给call(),因为方法签名不允许这样做。

    但是,您可以将必要的信息作为构造函数参数传递;例如

    public class DoPing implements Callable<String>{
        private final String ipToPing;
    
        public DoPing(String ipToPing) {
            this.ipToPing = ipToPing;
        }
    
        public String call() throws SomeException {
            InetAddress ipAddress = InetAddress.getByName(ipToPing);
            ....
        }
    }
    

    (我已经纠正了几个严重的代码风格违规!!)

    有一些方法可以消除上面的一些“样板”编码(参见其他一些答案)。在这种情况下,我们谈论的是 4 行代码(在大约 40 行的类中),所以我不相信这值得付出努力。 (但是,嘿,这是你的代码。)

    或者,您可以:

    • 将 DoPing 声明为内部类(或 lambda)并让它在封闭范围内引用 final ipToPing,或

    • 添加一个setIpToPing(String ipToPing) 方法。

    (最后一个允许重用 DoPing 对象,但缺点是您需要同步才能线程安全地访问它。)

    【讨论】:

      【解决方案2】:

      添加到 Jarle 的答案 - 如果您创建 Callable 作为匿名类的实例,您可以使用匿名类之外的 final 字段将数据传递到实例:

          final int arg = 64;
          executor.submit(new Callable<Integer>() {
              public Integer call() throws Exception {
                  return arg * 2;
              }
          });
      

      【讨论】:

        【解决方案3】:

        您不能将参数传递给call(),因为方法签名不允许这样做,但这里至少有一种解决方法

        1. 定义一个抽象类来包装/实现Callable
        2. 实现一个设置器将结果“注入”到call()

        定义一个抽象类:

        import java.util.concurrent.Callable;
        
        public abstract class Callback<T> implements Callable<Void> {
            T result;
        
            void setResult (T result) {
                this.result = result;
            }
        
            public abstract Void call ();
        }
        

        定义触发回调的方法:

        public void iWillFireTheCallback (Callback callback) {
            // You could also specify the signature like so:
            // Callback<Type of result> callback
        
            // make some information ("the result")
            // available to the callback function:
            callback.setResult("Some result");
        
            // fire the callback:
            callback.call();
        }
        

        在你想打电话的地方iWillFireTheCallback

        定义回调函数(甚至可以在方法内部):

        class MyCallback extends Callback {
            @Override
            public Void call () {
                // this is the actual callback function
        
                // the result variable is available right away:
                Log.d("Callback", "The result is: " + result);
        
                return null;
            }
        }
        

        然后在传入回调的同时调用iWillFireTheCallback

        iWillFireTheCallback(new MyCallback());
        

        【讨论】:

          【解决方案4】:

          当你创建 doPing-class 时(应该是类名中的大写字母),在构造函数中发送 ip-address。在调用方法中使用此 IP 地址。

          【讨论】:

            【解决方案5】:

            将一些 (final) 字段放入您的 doPing 类中,以及初始化它们的构造函数,然后将您要在 call() 中使用的值传递给 doPing 的构造函数:

            public class DoPing implements Callable<String>  {
                 private final String ipToPing;
            
                 public DoPing(String ip) {
                     this.ipToPing = ip;
                 }
                 
                 public String call() {
                     // use ipToPing
                 }
            }
            

            【讨论】:

              【解决方案6】:

              您必须定义一个属性,例如ipAddress 及其访问器方法。并在constructorsetter 方法中传递其值。在doPing 类中使用ipAddress 属性。

              class DoPing/* In java all classes start with capital letter */implements Callable<String>
              {
                  private String  ipAddress;
              
                  public String getIpAddress()
                  {
                      return ipAddress;
                  }
              
                  public void setIpAddress(String ipAddress)
                  {
                      this.ipAddress = ipAddress;
                  }
              
                  /*
                   * Counstructor 
                   */
                  public DoPing(String ipAddress )
                  {
                      this.ipAddress = ipAddress;
                  }
              
                  @Override
                  public String call() throws Exception
                  {
                      // your logic
                  }
              }
              

              【讨论】:

                【解决方案7】:

                我知道现在回答这个问题太晚了,考虑到它已有 8 年多的历史但在 15 天前(!)活跃,我觉得这仍然会帮助使用 Java 8 及更高版本的人。

                PS,它只是 Victor Sorokin 答案的语法糖,可以通过 lambdas 实现。

                public static Callable<String> generateCallableWithArg(final String input) {
                    return () -> {
                      Thread.sleep(5000); // someExpensiveOperationHere
                      return "Return Value of " + input; //input can be used here
                    };
                  }
                

                另外,我们可以编写一个静态辅助方法,可以将 Function 转换为 Callable。

                public class CallableGenerator {
                
                  public static <T,V> Callable<V> getCallableFromFunction(Function<T, V> function, T input) {
                    return () -> function.apply(input);
                  }
                }
                

                这可以用作

                Callable<Integer> iAmCallable = CallableGenerator.getCallableFromFunction(i1 -> i1 * 2, 3);
                

                【讨论】:

                  【解决方案8】:

                  并不总是可以(有效地)引用最终变量以将其值用作“参数”,但您可以自己制作舒适的通用解决方案。先定义这个功能接口:

                  @FunctionalInteface
                  interface CallableFunction<T, R> {
                  
                      public abstract R call(T arg) throws Exception;
                  
                      public static <T, R> Callable<R> callable(CallableFunction<T, R> cf, T arg) {
                          return () -> cf.call(arg);
                      }
                  }
                  

                  此功能接口提供静态方法callable,它创建一个Callable 实例,该实例简单地调用call(T) 并带有提供的参数(T 类型)。然后你需要你 DoPing 类来实现 CallableFunction 像这样:

                  public class DoPing implements CallableFunction<String, String> {
                  
                      @Override
                      public String call(final String ipToPing) throws Exception {
                          final var ipAddress = InetAddress.getByName(ipToPing);
                          final var reachable = ipAddress.isReachable(1400);
                          String pingOutput = null;
                          if (reachable) {
                              pingOutput = ipToPing + " is reachable.\n";
                          }
                          else {
                              final var ping = Runtime.getRuntime().exec("ping " + ipToPing + " -n 1 -w 300");
                              try (var in = new BufferedReader(new InputStreamReader(ping.getInputStream()))) {
                                  String line;
                                  for (int lineCount = 1; (line = in.readLine()) != null; ++lineCount) {
                                      if (lineCount == 3) {
                                          pingOutput = "Ping to " + ipToPing + ": " + line + "\n";
                                          break;
                                      }
                                  }
                              }
                          }
                          return pingOutput;
                      }
                  

                  在这里,我们将call 签名更改为接受String 参数,现在它实现了CallableFunction,而不是像以前那样实现Callable。其他变化很小,但值得一提的是,我们通过在 BufferedReader 上使用 try-with-resource 来防止资源泄漏,并且 break 已添加到输入收集循环中(从 while 更改为 for)尽快终止。

                  现在您可以使用代码,例如像这样:

                      final var ping = CallableFunction.callable(new DoPing(), "127.0.0.1");
                      final var task = new FutureTask<>(ping);
                      new Thread(task).start();
                      System.out.println(task.get(20, TimeUnit.SECONDS));
                  

                  您还可以在需要时在其他情况下重复使用CallableFunction

                  【讨论】:

                    猜你喜欢
                    • 2023-03-21
                    • 2020-09-03
                    • 1970-01-01
                    • 2012-03-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2015-07-30
                    • 1970-01-01
                    相关资源
                    最近更新 更多