|
| 1 | +/* |
| 2 | + * Copyright (C) 2012-2015 Typesafe Inc. <http://www.typesafe.com> |
| 3 | + */ |
| 4 | +package scala.compat.java8; |
| 5 | + |
| 6 | +import org.apache.commons.lang3.SerializationUtils; |
| 7 | +import scala.runtime.*; |
| 8 | + |
| 9 | +import static junit.framework.Assert.assertEquals; |
| 10 | +import static scala.compat.java8.JFunction.*; |
| 11 | +import static scala.compat.java8.TestAPI.*; |
| 12 | + |
| 13 | +import org.junit.Test; |
| 14 | + |
| 15 | + |
| 16 | +public class LambdaTest { |
| 17 | + @Test |
| 18 | + public void lambdaDemo() { |
| 19 | + // Scala 2.12+ only: |
| 20 | + //scala.Function1<String, String> f1 = (String s) -> s; |
| 21 | + |
| 22 | + // Not allowed with Scala 2.10 nor 2.11 |
| 23 | + // "incompatible types: Function1 is not a functional interface" |
| 24 | + // scala.Function1<String, String> f = (String s) -> s; |
| 25 | + |
| 26 | + // Function1 is not a functional interface because it has abstract |
| 27 | + // methods in addition to apply, namely `compose` and `andThen` |
| 28 | + // (which are implemented in Scala-derived subclasses with mixin |
| 29 | + // inheritance), and the specialized variants of apply (also provided |
| 30 | + // by scalac.) |
| 31 | + |
| 32 | + // That's a pity, but we can get pretty close with this library! |
| 33 | + |
| 34 | + // We have to tell javac to use `JFunction1` as the functional interface. |
| 35 | + // Scala 2.12 does not have or need JFunction anymore. We provide it as a |
| 36 | + // deprecated stub for backwards compatibility. Use `scala.Function1` for |
| 37 | + // code that targets Scala 2.12+ exclusively. |
| 38 | + JFunction1<String, String> f1 = (String s) -> s; |
| 39 | + |
| 40 | + // That's more or less equivalent to the old, anonymous class syntax: |
| 41 | + new JFunction1<String, String>() { |
| 42 | + public String apply(String s) { return s; } |
| 43 | + }; |
| 44 | + |
| 45 | + // You might have seen this form before: |
| 46 | + new AbstractFunction1<String, String>() { |
| 47 | + public String apply(String s) { return s; } |
| 48 | + }; |
| 49 | + |
| 50 | + // However, we can't use `AbstractFunction1` as a functional interface |
| 51 | + // as it is a class. Further |
| 52 | + |
| 53 | + // F1 is a subclass of Function1: |
| 54 | + scala.Function1<String, String> f2 = f1; |
| 55 | + |
| 56 | + // Factory methods in `JFunction` can reduce the verbosity a little: |
| 57 | + // `func` is actually just an identity method; it only exists to |
| 58 | + // trigger lambda creation using the `JFunction1` functional interface. |
| 59 | + scala.Function1<String, String> f3 = func((String s) -> s); |
| 60 | + |
| 61 | + // Note that javac's type inference can infer the parameter type here, |
| 62 | + // based on the acribed type of `f4`. |
| 63 | + scala.Function1<String, String> f4 = func(s -> s); |
| 64 | + |
| 65 | + // f1.apply(""); |
| 66 | + |
| 67 | + // Specialized variants of the `apply` method are provided but implementing a specialized |
| 68 | + // Scala function in this straight-forward way results in boxing and unboxing because the |
| 69 | + // Java lambda operates on boxed types: |
| 70 | + JFunction1<Integer, Integer> f5 = (i) -> -i; |
| 71 | + assert(f5.apply(1) == -1); |
| 72 | + assert(f5.apply$mcII$sp(1) == -1); |
| 73 | + |
| 74 | + // We provide `JFunction.funcSpecialized` and `JFunction.procSpecialized` methods to avoid |
| 75 | + // boxing: |
| 76 | + scala.Function1<Integer, Integer> f5b = funcSpecialized((int i) -> -i); |
| 77 | + assert(f5b.apply(1) == -1); |
| 78 | + assert(f5b.apply$mcII$sp(1) == -1); |
| 79 | + |
| 80 | + // as are `curried`, `tupled`, `compose`, `andThen`. |
| 81 | + f3.compose(f3).andThen(f3).apply(""); |
| 82 | + scala.Function2<String, String, String> f6 = func((s1, s2) -> join(s1, s2)); |
| 83 | + assert(f6.curried().apply("1").apply("2").equals("12")); |
| 84 | + |
| 85 | + // Functions returning unit can use the `JProcedure1`, ... functional interfaces |
| 86 | + // in order to convert a void lamdba return to Scala's Unit: |
| 87 | + JProcedure1<String> f7b = s -> sideEffect(); |
| 88 | + scala.Function1<String, BoxedUnit> f7c = f7b; |
| 89 | + |
| 90 | + // The easiest way to do this is via `JFunction.proc`, .... |
| 91 | + // |
| 92 | + // Note that the lambda has a return type of `void` if the last |
| 93 | + // statement is a call to a `void` returning method, or if it is |
| 94 | + // a `return`. |
| 95 | + scala.Function1<String, BoxedUnit> f7 = proc(s -> sideEffect()); |
| 96 | + scala.Function1<String, BoxedUnit> f8 = proc(s -> {s.toUpperCase(); return;}); |
| 97 | + |
| 98 | + // Function0 is also available |
| 99 | + scala.Function0<String> f9 = func(() -> "42"); |
| 100 | + assert(f9.apply().equals("42")); |
| 101 | + |
| 102 | + // You can go up to 22 (the highest arity function defined in the Scala standard library.) |
| 103 | + assert(acceptFunction1(func(v1 -> v1.toUpperCase())).equals("1")); |
| 104 | + acceptFunction1Unit(proc(v1 -> sideEffect())); |
| 105 | + acceptFunction1Unit(proc(v1 -> {v1.toUpperCase(); return;})); |
| 106 | + |
| 107 | + assert(acceptFunction2(func((v1, v2) -> join(v1, v2))).equals("12")); |
| 108 | + acceptFunction2Unit(proc((v1, v2) -> {v1.toUpperCase(); return;})); |
| 109 | + |
| 110 | + assert(acceptFunction3(func((v1, v2, v3) -> join(v1, v2, v3))).equals("123")); |
| 111 | + acceptFunction3Unit(proc((v1, v2, v3) -> {v1.toUpperCase(); return;})); |
| 112 | + |
| 113 | + assert(acceptFunction22(func((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) -> join(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22))).equals("12345678910111213141516171819202122")); |
| 114 | + acceptFunction22Unit( proc((v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22) -> {v1.toUpperCase(); return;})); |
| 115 | + } |
| 116 | + |
| 117 | + @Test |
| 118 | + public void isSerializable() { |
| 119 | + JFunction0<String> f0 = () -> "foo"; |
| 120 | + assertEquals("foo", SerializationUtils.clone(f0).apply()); |
| 121 | + |
| 122 | + JFunction1<String, String> f1 = (a) -> a.toUpperCase(); |
| 123 | + assertEquals("FOO", SerializationUtils.clone(f1).apply("foo")); |
| 124 | + |
| 125 | + JFunction2<String, String, String> f2 = (a, b) -> a + b; |
| 126 | + assertEquals("foobar", SerializationUtils.clone(f2).apply("foo", "bar")); |
| 127 | + |
| 128 | + JFunction3<String, String, String, String> f3 = (a, b, c) -> a + b + c; |
| 129 | + assertEquals("foobarbaz", SerializationUtils.clone(f3).apply("foo", "bar", "baz")); |
| 130 | + } |
| 131 | + |
| 132 | + private static scala.concurrent.Future<Integer> futureExample( |
| 133 | + scala.concurrent.Future<String> future, scala.concurrent.ExecutionContext ec) { |
| 134 | + return future.map(func(s -> s.toUpperCase()), ec).map(func(s -> s.length()), ec); |
| 135 | + } |
| 136 | + |
| 137 | + private static void sideEffect() { |
| 138 | + } |
| 139 | + |
| 140 | + private static String join(String... ss) { |
| 141 | + String result = ""; |
| 142 | + for (String s : ss) { |
| 143 | + result = result + s; |
| 144 | + } |
| 145 | + return result; |
| 146 | + } |
| 147 | +} |
0 commit comments