我创建了一段代码,它获取一个 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 曾经是人们所采取的论点。
您不能将其作为参数传递给
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
对象,但缺点是您需要同步才能线程安全地访问它。)
添加 Jarle 的答案 - 如果您创建
Callable
作为匿名类的实例,您可以使用匿名类外部的 final
字段将数据传递到实例中:
final int arg = 64;
executor.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return arg * 2;
}
});
您无法将参数传递给
call()
,因为方法签名不允许这样做,但这里至少有一种方法可以解决这个问题:
Callable
和 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());
当您创建 doPing 类(类名应为大写字母)时,请在构造函数中发送 IP 地址。在调用方法中使用此 IP 地址。
在
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
}
}
我知道现在回答这个问题已经太晚了,考虑到它已有 8 年多了,但在 15 天(!)前就活跃了,我觉得这仍然会对使用 Java 8 及更高版本的人有所帮助。
PS,它只是 Victor Sorokin 的答案的语法糖,可以通过 lambda 实现。
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);
您必须定义诸如
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
}
}
并不总是可以引用(有效)最终变量以将其值用作“参数”,但您可以自己制定舒适的通用解决方案。首先定义这个功能接口:
@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
实例,该实例只需使用提供的参数(类型为 T)调用 call(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
。
如果您创建自己的自定义可调用对象,与 Callable 非常相似,除了 call() 根据您的情况接受参数,因此与 Callable 不兼容。当然,它可能不适用于很多情况,但为了简单起见,它可能是一个值得考虑的选择。
package com.custom
@FunctionalInterface
public interface CallableArgs<T,T1,T2> {
T call(T1 argument1, T2 argument2) throws Exception;
}