[英]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.