簡體   English   中英

varargs作為java 8中函數的輸入參數

[英]varargs as input parameter to a function in java 8

在Java 8中,如何定義函數以適合varargs。

我們有這樣的功能:

private String doSomethingWithArray(String... a){
   //// do something
   return "";
}

由於某種原因,我需要使用Java 8函數調用它(因為'andThen'可以與其他函數一起使用。)

因此我想定義下面給出的東西。

Function<String... , String> doWork = a-> doSomethingWithArray(a)  ;

這給了我編譯錯誤。以下工作,但輸入現在必須是一個數組,不能是一個單獨的字符串。

Function<String[] , String> doWork = a-> doSomethingWithArray(a)  ;

這里我提到了String,但它可以是任何Object的數組。

有沒有辦法使用varargs(...)而不是array([])作為輸入參數?

或者,如果我創建一個類似於Function的新界面,是否可以創建類似下面的內容?

@FunctionalInterface
interface MyFunction<T... , R> {
//..
} 

在這種情況下,您不能使用varargs語法,因為它不是方法參數。

根據您使用的Function類型,您可能根本不需要它,您可以按原樣使用您的方法,而無需通過功能接口引用它們。

作為替代方案,您可以定義自己的功能界面,如下所示:

@FunctionalInterface
public interface MyFunctionalInterface<T, R> {
    R apply(T... args);
}

然后你的聲明變成:

MyFunctionalInterface<String, String> doWork = a -> doSomethingWithArray(a);

並且調用doWork現在可以是:

String one = doWork.apply("one");
String two = doWork.apply("one","two");
String three = doWork.apply("one","two","three");
...
...

注意 - 功能接口名稱只是一個占位符,可以進行改進,以與功能接口的Java命名約定保持一致,例如VarArgFunction或類似的東西。

因為數組和變量是覆蓋等價的,所以可以使用以下內容:

@FunctionalInterface
interface VarArgsFunction<T, U> extends Function<T[], U> {
    @Override
    U apply(T... args);
}

// elsewhere
VarArgsFunction<String, String> toString =
    args -> Arrays.toString(args);
String str = toString.apply("a", "b", "c");
// and we could pass it to somewhere expecting
// a Function<String[], String>

也就是說,這與一般調用該方法有關。 以下引發ClassCastException

static void invokeApply() {
    VarArgsFunction<Double, List<Double>> fn =
        Arrays::asList;
    List<Double> list = invokeApply(fn, 1.0, 2.0, 3.0);
}
static <T, U> U invokeApply(VarArgsFunction<T, U> fn,
                            T arg0, T arg1, T arg2) {
    return fn.apply(arg0, arg1, arg2); // throws an exception
}

示例在行動。

這是因為類型擦除:調用apply方法通常會創建一個數組,其組件類型是類型變量T的擦除。 在上面的例子中,由於類型變量T的擦除是Object ,它創建Object[]數組並將其傳遞給期望Double[]apply方法。

使用泛型varargs覆蓋apply方法(更常見的是編寫任何通用的varargs方法)將生成警告,這就是原因。 (該警告在JLS的8.4.1中有規定。)

因此,我實際上並不建議使用它。 我發布它是因為,它很有趣,它確實在更簡單的情況下工作,我想解釋為什么它可能不應該被使用。

簡短的回答

這似乎不可能。 Function接口只有四個方法,這些方法都沒有采用vararg參數。

擴展功能界面?

也不起作用。 由於數組在Java中是一些奇怪的低級構造,因為類型擦除它們不能很好地處理泛型類型。 特別是,無法使用Class<X> -reflection-thingies創建一個泛型類型的數組而不會污染整個代碼庫。 因此,使用default方法擴展Function<X, Y>接口甚至不可行,該方法需要使用varargs和重定向來apply

數組創建語法,輔助方法

如果您靜態地知道參數的類型,那么您可以做的最好的事情是使用內聯語法進行數組創建:

myFunction.apply(new KnownType[]{x, y, z});

而不是你想要的varargs:

myFunction.apply(x, y, z); // doesn't work this way

如果這太長了,你可以定義一個輔助函數來從varargs創建KnownType數組:

// "known type array"
static KnownType[] kta(KnownType... xs) {
    return xs;
}

然后按如下方式使用它:

myFunction.apply(kta(x, y, z, w))

這至少會更容易打字和閱讀。

嵌套方法,真正的變種

如果你真的(我的意思是, 真的 )想要使用vararg -syntax將已知類型的參數傳遞給黑盒泛型Function ,那么你需要類似嵌套方法的東西。 所以,例如,如果你想擁有這個:

myHigherOrderFunction(Function<X[], Y> blah) {
  X x1 = ... // whatever
  X x2 = ... // more `X`s
  blah(x1, x2) // call to vararg, does not work like this!
}

您可以使用來模擬嵌套函數:

import java.util.function.*;

class FunctionToVararg {

    public static double foo(Function<int[], Double> f) {
      // suppose we REALLY want to use a vararg-version
      // of `f` here, for example because we have to 
      // use it thousand times, and inline array 
      // syntax would be extremely annoying.

      // We can use inner nested classes.
      // All we really need is one method of the
      // nested class, in this case.
      class Helper {
        // The inner usage takes array, 
        // but `fVararg` takes varargs!
        double fVararg(int... xs) {
          return f.apply(xs);
        }
        double solveTheActualProblem() {
          // hundreds and hundreds of lines 
          // of code with dozens of invokations
          // of `fVararg`, otherwise it won't pay off
          // ...
          double blah = fVararg(40, 41, 43, 44);
          return blah;
        }
      }

      return (new Helper()).solveTheActualProblem();
    }

    public static void main(String[] args) {
      Function<int[], Double> example = ints -> {
        double d = 0.0;
        for (int i: ints) d += i;
        return d / ints.length;
      };

      System.out.println(foo(example)); // should give `42`
    }
}

如你所見,這是一個很大的痛苦。 是不是真的值得嗎?

結論

總的來說,這似乎是一個想法,無論你做什么,用Java實現都會非常痛苦。 至少我沒有看到任何簡單的解決方案。 說實話,我也沒有看到它真正需要的地方(也許只是我和BLUB-paradox)。

將varargs方法定位到強類型Function一種安全方法是使用一種稱為currying的技術。

例如,如果您需要使用3個參數定位varargs方法,則可以按如下方式執行:

Function<String, Function<String, Function<String, String>>> doWork =
    a1 -> a2 -> a3 -> doSomethingWithArray(a1, a2, a3);

然后,無論您需要調用該函數:

String result = doWork.apply("a").apply("b").apply("c");

此技術不僅適用於varargs方法,還適用於具有任意數量不同類型參數的任何方法。

如果您已經有一個帶參數的數組,只需使用Function<String[], String>

Function<String[], String> doWork = a -> doSomethingWithArray(a);

然后:

String[] args = {"a", "b", "c"};

String result = doWork.apply(args);

因此,只要有固定數量的參數,就可以使用currying。 每當你有動態參數(由數組表示)時,請使用最后一種方法。

不幸的是,添加一個方法來調解並為你做翻譯是我能想到的。

public class FunctionalTest {
   public static void main( String[] args ) {
      kludge( "a","b","c" );
   }

   private static Function<String[],PrintStream> ref = a -> System.out.printf( "", a );
   public static void kludge( String... y ) {
      ref.apply( y );
   }
}

暫無
暫無

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

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