48

我创建了一段代码,它获取一个 IP 地址(来自另一个类中的 main 方法),然后循环遍历一系列 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 曾经是采用的论点。

4

8 回答 8

68

您不能将它作为参数传递给,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在封闭范围内引用 a,或

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

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

于 2012-04-03T12:18:52.367 回答
7

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

    final int arg = 64;
    executor.submit(new Callable<Integer>() {
        public Integer call() throws Exception {
            return arg * 2;
        }
    });
于 2012-04-03T12:19:19.083 回答
6

您不能将参数传递给,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());
于 2016-06-14T13:59:35.040 回答
5

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

于 2012-04-03T12:14:07.773 回答
3

在您的类中放置一些 ( 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
     }
}
于 2012-04-03T12:17:54.003 回答
1

您必须定义一个属性,例如ipAddress及其访问器方法。constructor并通过方法或方法传递其值setter。在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
    }
}
于 2012-04-03T12:23:35.287 回答
1

我知道回答这个问题已经太晚了,考虑到它已经超过 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);
于 2021-01-22T19:11:14.083 回答
0

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

@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像以前那样。其他变化很小,但值得一提的是,我们通过使用 try-with-resource on 来防止资源泄漏,BufferedReader并且还break添加到输入收集循环(从 更改whilefor)以尽快终止。

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

    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在需要时在其他情况下重复使用。

于 2020-08-20T22:03:38.383 回答