簡體   English   中英

有沒有辦法在可調用方法中接受參數?

[英]Is there a way to take an argument in a callable method?

我創建了一段代碼,它獲取一個 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()因為方法簽名不允許它但這里至少有一種方法可以解決這個問題

  1. 定義一個抽象類來包裝/實現Callable
  2. 實現一個 setter 將結果“注入”到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-address。 在 call-method 中使用這個 ip-address。

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
     }
}

你必須defien屬性,如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
    }
}

並非總是可以引用(有效)最終變量以將其值用作“參數”,但您可以自己制定舒適的通用解決方案。 首先定義這個功能接口:

@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) 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

我知道現在回答這個問題太晚了,考慮到它已經有 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);

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM