简体   繁体   English

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

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

In Java 8, how is a Function is defined to fit varargs. 在Java 8中,如何定义函数以适合varargs。

we have a function like this: 我们有这样的功能:

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

And for some reason I need to call it using Java 8 function (because 'andThen' can be used along with other functions.) 由于某种原因,我需要使用Java 8函数调用它(因为'andThen'可以与其他函数一起使用。)

And thus I wanted to define it something as given below. 因此我想定义下面给出的东西。

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

That gives me compilation error.Following works, but input is now has to be an array and can not be a single string. 这给了我编译错误。以下工作,但输入现在必须是一个数组,不能是一个单独的字符串。

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

Here I mentioned String, but it can be an array of any Object. 这里我提到了String,但它可以是任何Object的数组。

Is there a way to use varargs(...)instead of array([]) as input parameter? 有没有办法使用varargs(...)而不是array([])作为输入参数?

Or if I create a new interface similar to Function, is it possible to create something like below? 或者,如果我创建一个类似于Function的新界面,是否可以创建类似下面的内容?

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

You cannot use the varargs syntax in this case as it's not a method parameter. 在这种情况下,您不能使用varargs语法,因为它不是方法参数。

Depending on what you're using the Function type for, you may not even need it at all and you can just work with your methods as they are without having to reference them through functional interfaces. 根据您使用的Function类型,您可能根本不需要它,您可以按原样使用您的方法,而无需通过功能接口引用它们。

As an alternative you can define your own functional interface like this: 作为替代方案,您可以定义自己的功能界面,如下所示:

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

then your declaration becomes: 然后你的声明变成:

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

and calling doWork can now be: 并且调用doWork现在可以是:

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

note - the functional interface name is just a placeholder and can be improved to be consistent with the Java naming convention for functional interfaces eg VarArgFunction or something of that ilk. 注意 - 功能接口名称只是一个占位符,可以进行改进,以与功能接口的Java命名约定保持一致,例如VarArgFunction或类似的东西。

Because arrays and varargs are override-equivalent, the following is possible : 因为数组和变量是覆盖等价的,所以可以使用以下内容:

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

That said, this has a pitfall having to do with invoking the method generically. 也就是说,这与一般调用该方法有关。 The following throws a ClassCastException : 以下引发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. ) 示例在行动。

This happens because of type erasure: invoking the apply method generically creates an array whose component type is the erasure of the type variable T . 这是因为类型擦除:调用apply方法通常会创建一个数组,其组件类型是类型变量T的擦除。 In the above example, since the erasure of the type variable T is Object , it creates and passes an Object[] array to the apply method which is expecting a Double[] . 在上面的例子中,由于类型变量T的擦除是Object ,它创建Object[]数组并将其传递给期望Double[]apply方法。

Overriding the apply method with generic varargs (and more generally writing any generic varargs method) will generate a warning and that's why. 使用泛型varargs覆盖apply方法(更常见的是编写任何通用的varargs方法)将生成警告,这就是原因。 (The warning is mandated in 8.4.1 of the JLS.) (该警告在JLS的8.4.1中有规定。)

Because of that, I don't actually recommend using this. 因此,我实际上并不建议使用它。 I've posted it because, well, it's interesting, it does work in simpler cases and I wanted to explain why it probably shouldn't be used. 我发布它是因为,它很有趣,它确实在更简单的情况下工作,我想解释为什么它可能不应该被使用。

Short answer 简短的回答

This doesn't seem possible. 这似乎不可能。 Function interface has only four methods, and none of those methods takes vararg arguments. Function接口只有四个方法,这些方法都没有采用vararg参数。

Extend Function interface? 扩展功能界面?

Doesn't work either. 也不起作用。 Since arrays are somewhat strange low-level constructs in Java, they do not work well with generic types because of type erasure. 由于数组在Java中是一些奇怪的低级构造,因为类型擦除它们不能很好地处理泛型类型。 In particular, it is not possible to create an array of generic type without contaminating your entire codebase with Class<X> -reflection-thingies. 特别是,无法使用Class<X> -reflection-thingies创建一个泛型类型的数组而不会污染整个代码库。 Therefore, it's not even feasible to extend the Function<X, Y> interface with a default method which takes varargs and redirects to apply . 因此,使用default方法扩展Function<X, Y>接口甚至不可行,该方法需要使用varargs和重定向来apply

Syntax for array creation, helper methods 数组创建语法,辅助方法

If you statically know the type of the arguments, then the best thing you can do is to use the inline syntax for array creation: 如果您静态地知道参数的类型,那么您可以做的最好的事情是使用内联语法进行数组创建:

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

instead of the varargs, which you want: 而不是你想要的varargs:

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

If this is too long, you could define a helper function for creation of arrays of KnownType from varargs: 如果这太长了,你可以定义一个辅助函数来从varargs创建KnownType数组:

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

and then use it as follows: 然后按如下方式使用它:

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

which would at least be somewhat easier to type and to read. 这至少会更容易打字和阅读。

Nested methods, real varargs 嵌套方法,真正的变种

If you really (I mean, really ) want to pass arguments of known type to a black-box generic Function using the vararg -syntax, then you need something like nested methods. 如果你真的(我的意思是, 真的 )想要使用vararg -syntax将已知类型的参数传递给黑盒泛型Function ,那么你需要类似嵌套方法的东西。 So, for example, if you want to have this: 所以,例如,如果你想拥有这个:

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

you could use classes to emulate nested functions: 您可以使用来模拟嵌套函数:

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

As you see, that's a lot of pain. 如你所见,这是一个很大的痛苦。 Is it really worth it? 是不是真的值得吗?

Conclusion 结论

Overall, this seems to be an idea which would be extremely painful to implement in Java, no matter what you do. 总的来说,这似乎是一个想法,无论你做什么,用Java实现都会非常痛苦。 At least I don't see any simple solutions. 至少我没有看到任何简单的解决方案。 To be honest, I also don't see where it would be really necessary (maybe it's just me vs. the BLUB-paradox). 说实话,我也没有看到它真正需要的地方(也许只是我和BLUB-paradox)。

One safe way to target a varargs method to a strongly typed Function is by using a technique called currying . 将varargs方法定位到强类型Function一种安全方法是使用一种称为currying的技术。

For example, if you need to target your varargs method with 3 arguments, you could do it as follows: 例如,如果您需要使用3个参数定位varargs方法,则可以按如下方式执行:

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

Then, wherever you need to call the function: 然后,无论您需要调用该函数:

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

This technique works to target not only varargs methods, but also any method with any number of arguments of different types. 此技术不仅适用于varargs方法,还适用于具有任意数量不同类型参数的任何方法。

If you already have an array with the arguments, just use a Function<String[], String> : 如果您已经有一个带参数的数组,只需使用Function<String[], String>

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

And then: 然后:

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

String result = doWork.apply(args);

So, whenever you have a fixed number of arguments, use currying. 因此,只要有固定数量的参数,就可以使用currying。 And whenever you have dynamic arguments (represented by an array), use this last approach. 每当你有动态参数(由数组表示)时,请使用最后一种方法。

Unfortunately, adding a method to intercede and do the translation for you was all I could come up with. 不幸的是,添加一个方法来调解并为你做翻译是我能想到的。

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