简体   繁体   English

有没有办法在可调用方法中接受参数?

[英]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()因为方法签名不允许它但这里至少有一种方法可以解决这个问题

  1. defining an abstract class that wraps/implements Callable and定义一个抽象类来包装/实现Callable
  2. implementing a setter to "inject" a result into 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.并在constructorsetter方法中传递其值。 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.

相关问题 如何使用Java反射调用以可调用接口或方法作为参数的方法 - How to Invoke a method that take a Callable Interface or method as parameter with java reflection Java 8 中的注解中取方法参数作为参数 - Take method argument as an argument in the annotation in Java 8 Java-在运行时按参数类选择可调用方法的最佳选项 - Java - best option to select callable method at runtime by argument class 实际参数列表 <anonymous Callable<String> &gt;&gt;无法转换为列表 <Callable<String> &gt;通过方法调用转换 - actual argument List<<anonymous Callable<String>>> cannot be converted to List<Callable<String>> by method invocation conversion 在Java中,可以将一行代码作为方法参数? - In Java, it is possible to take a line of code as a method argument? toString()方法可以在Java中接受参数吗? - Can toString() method take an argument in Java? 带参数的可调用lambda表达式 - Callable lambda expression with argument Java如何将字符作为参数,然后该方法前后打印顺序? - Java how to take chars as an argument and then the method prints the sequence forwards and backwards? Java方法如何接受扩展通用类型的参数 - How can a java method take an argument that extends a generic type 我是否可以使用可以将HashMap或列表作为参数的方法? - Is it possible for me to have a method that can take both a HashMap or a list as an argument?
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM