varargs作为java 8中函数的输入参数

时间:2018-01-20 00:51:20

标签: java java-8

在Java 8中,如何定义函数以适应varargs。

我们有这样的功能:

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

由于某种原因,我需要使用Java 8函数调用它(因为'然后'可以与其他函数一起使用。)

因此我想在下面给出一些定义。

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

这给了我编译错误。以下工作,但输入现在必须是一个数组,不能是一个字符串。

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

这里我提到了String,但它可以是任何Object的数组。

有没有办法使用varargs(...)而不是array([])作为输入参数?

或者,如果我创建一个类似于Function的新界面,是否可以创建类似下面的内容?

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

5 个答案:

答案 0 :(得分:7)

在这种情况下,您不能使用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");
...
...

note - 功能接口名称只是一个占位符,可以改进以与功能接口的Java命名约定保持一致,例如VarArgFunction或类似的东西。

答案 1 :(得分:4)

因为数组和变量是覆盖等价的,所以可能

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

Example in action.

这是因为类型擦除:调用apply方法通常会创建一个数组,其组件类型是类型变量T的擦除。在上面的示例中,由于类型变量T的擦除是Object,因此它创建并将Object[]数组传递给期望{{1}的apply方法}}

使用泛型varargs覆盖Double[]方法(更常见的是编写任何通用的varargs方法)将生成警告,这就是原因。 (该警告是在JLS的8.4.1中强制执行的。)

因此,我实际上并不建议使用此功能。我发布了它,因为它很有趣,它确实在更简单的情况下工作,我想解释它为什么不应该被使用。

答案 2 :(得分:1)

简短回答

这似乎不可能。 Function接口只有四个方法,这些方法都不需要vararg个参数。

扩展功能界面?

也不起作用。由于数组在Java中是一些奇怪的低级构造,因为类型擦除它们不能很好地处理泛型类型。特别是,不可能创建一个泛型类型的数组,而不会使用Class<X> - reflection-thingies污染整个代码库。因此,使用Function<X, Y>方法扩展default接口甚至不可行,该方法需要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))

这至少会更容易打字和阅读。

嵌套方法,真正的变种

如果你真的(我的意思是,真的)想要使用Function语法将已知类型的参数传递给黑盒子通用vararg,那么你需要类似于嵌套方法的东西。所以,例如,如果你想要这个:

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

答案 3 :(得分:1)

将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。每当你有动态参数(由数组表示)时,请使用最后一种方法。

答案 4 :(得分:0)

不幸的是,添加一种方法来代替并为你做翻译是我能想到的。

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