[英]Is there a way to take an argument in a callable method?
I have created a piece of code which takes an IP address (from main method in another class) and then loops through a range of IP addresses pinging each one as it goes.我创建了一段代码,它获取一个 IP 地址(来自另一个类中的 main 方法),然后循环遍历一系列 IP 地址,并在执行过程中对每个地址进行 ping 操作。 I have a GUI front end on this and it was crashing (hence why I've done the multithreading. My problem is I can no longer take the IP address as an argument in my ping code as its callable. I've searched all over for this and cant seem to find a way to get round this. Is there a way for a callable method to take arguments? If not is there any other way to accomplish what I'm trying to do?我有一个 GUI 前端,它崩溃了(因此我做了多线程处理。我的问题是我不能再将 IP 地址作为我的 ping 代码中的参数作为它的可调用参数。我已经搜索过了为此,似乎无法找到解决此问题的方法。有没有办法让可调用方法接受参数?如果没有,还有其他方法可以完成我想要做的事情吗?
sample of my code:我的代码示例:
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 used to be the argument that was taken. IPtoPing 曾经是采用的参数。
You can't pass it as the argument to call()
because the method signature doesn't allow it.您不能将它作为参数传递给call()
因为方法签名不允许它。
However, you can pass the necessary information as a constructor argument;但是,您可以将必要的信息作为构造函数参数传递; eg例如
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);
....
}
}
(I've corrected a couple of egregious code style violations!!) (我已经纠正了一些严重的代码风格违规行为!!)
There are ways to eliminate some of the "boilerplate" coding in the above (see some of the other answers).有一些方法可以消除上面的一些“样板”编码(请参阅其他一些答案)。 In this case we are talking about 4 lines of code (in a ~40 line class), so I am not convinced that it is worth the effort.在这种情况下,我们谈论的是 4 行代码(在大约 40 行的类中),所以我不相信这是值得的。 (But hey, it is your code.) (但是,嘿,这是您的代码。)
Alternatively, you could:或者,您可以:
declare DoPing as an inner class (or a lambda) and have it refer to a final ipToPing
in the enclosing scope, or将 DoPing 声明为内部类(或 lambda)并让它引用封闭范围内的final ipToPing
,或
add a setIpToPing(String ipToPing)
method.添加一个setIpToPing(String ipToPing)
方法。
(The last allows a DoPing
object to be reused, but the downside is that you will need to synchronize to access it thread-safely.) (最后一个允许重复使用DoPing
对象,但缺点是您需要同步以线程安全地访问它。)
Adding to Jarle's answer -- in case you create Callable
as instance of anonymous class, you can use final
field outside of anonymous class for passing data into the instance:添加到 Jarle 的答案 - 如果您将Callable
创建为匿名类的实例,则可以使用匿名类之外的final
字段将数据传递到实例中:
final int arg = 64;
executor.submit(new Callable<Integer>() {
public Integer call() throws Exception {
return arg * 2;
}
});
You can't pass arguments to call()
because the method signature doesn't allow it but here is at least one way to work around that by您不能将参数传递给call()
因为方法签名不允许它但这里至少有一种方法可以解决这个问题
Callable
and定义一个抽象类来包装/实现Callable
和call()
实现一个 setter 将结果“注入”到call()
Define an abstract class:定义一个抽象类:
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 ();
}
Define the method that should fire the callback:定义应该触发回调的方法:
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();
}
In the place where you want to call iWillFireTheCallback
:在您要调用iWillFireTheCallback
的地方:
Define the callback function (even possible inside methods):定义回调函数(甚至可能在方法内部):
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;
}
}
And then call iWillFireTheCallback
while passing in the callback:然后在传入回调时调用iWillFireTheCallback
:
iWillFireTheCallback(new MyCallback());
When you create the doPing-class (should be captial letter in class name), send in the ip-address in the constructor.创建 doPing 类时(类名中应该是大写字母),在构造函数中发送 ip-address。 Use this ip-address in the call-method.在 call-method 中使用这个 ip-address。
Put some ( final
) fields in your doPing
class, and a constructor that initializes them, then pass the values you want to use in call()
to the constructor of doPing
:在doPing
类中放置一些( final
)字段,以及初始化它们的构造函数,然后将要在call()
使用的值传递给doPing
的构造doPing
:
public class DoPing implements Callable<String> {
private final String ipToPing;
public DoPing(String ip) {
this.ipToPing = ip;
}
public String call() {
// use ipToPing
}
}
You have to defien a property such as ipAddress
and its accessor method.你必须defien属性,如ipAddress
和其存取方法。 and passing its value in constructor
or by setter
method.并在constructor
或setter
方法中传递其值。 In doPing
class use ipAddress
property.在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
}
}
It is not always possible to make reference to (effectively) final variable to use its value as "argument", but you can make comfy general solution by yourself.并非总是可以引用(有效)最终变量以将其值用作“参数”,但您可以自己制定舒适的通用解决方案。 First define this functional interface:首先定义这个功能接口:
@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);
}
}
This functional interface provides static method callable
that creates a Callable
instance, which simply calls call(T)
with provided argument (of type T).这个函数式接口提供了静态方法callable
,它创建了一个Callable
实例,它简单地使用提供的参数(T 类型)调用call(T)
T)。 Then you need you DoPing
class to implement CallableFunction
like this:然后你需要你的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;
}
Here we changed call
signature to accept String
argument and also now it implements CallableFunction
and not Callable
as before.在这里,我们将call
签名更改为接受String
参数,并且现在它实现了CallableFunction
而不是像以前那样Callable
。 Other changes are minor, but it's worth mentioning, that we prevented resource leak with use of try-with-resource on BufferedReader
and also break
has been added to input collecting loop (change from while
to for
) to terminate as quickly, as possible.其他更改很小,但值得一提的是,我们通过在BufferedReader
上使用 try-with-resource 来防止资源泄漏,并且还向输入收集循环添加了break
(从while
更改为for
)以尽可能快地终止。
Now you can use the code eg like this:现在你可以像这样使用代码:
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));
You can also reuse CallableFunction
in other cases, when you needed it.您还可以在需要时在其他情况下重用CallableFunction
。
I know it is super-late to answer this, considering it is more than 8 years old but active 15 days(!) ago, I feel this will still help someone using Java 8 and above.我知道现在回答这个问题太晚了,考虑到它已经有 8 年历史了,但在 15 天(!)前活跃,我觉得这仍然会对使用 Java 8 及更高版本的人有所帮助。
PS, it is simply a syntactic sugar of Victor Sorokin's answer possible through lambdas. 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
};
}
Also, we can write a static helper method that can convert a Function to Callable.此外,我们可以编写一个静态辅助方法,可以将 Function 转换为 Callable。
public class CallableGenerator {
public static <T,V> Callable<V> getCallableFromFunction(Function<T, V> function, T input) {
return () -> function.apply(input);
}
}
This can be used as这可以用作
Callable<Integer> iAmCallable = CallableGenerator.getCallableFromFunction(i1 -> i1 * 2, 3);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.