• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Licensed to the Apache Software Foundation (ASF) under one or more
3  * contributor license agreements.  See the NOTICE file distributed with
4  * this work for additional information regarding copyright ownership.
5  * The ASF licenses this file to You under the Apache License, Version 2.0
6  * (the "License"); you may not use this file except in compliance with
7  * the License.  You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package org.apache.commons.lang3.function;
19 
20 import java.lang.invoke.MethodHandle;
21 import java.lang.invoke.MethodHandleProxies;
22 import java.lang.invoke.MethodHandles;
23 import java.lang.reflect.Method;
24 import java.util.Objects;
25 import java.util.function.BiConsumer;
26 import java.util.function.BiFunction;
27 import java.util.function.Function;
28 import java.util.function.Supplier;
29 
30 import org.apache.commons.lang3.exception.UncheckedIllegalAccessException;
31 
32 /**
33  * Converts {@link Method} objects to lambdas.
34  * <p>
35  * More specifically, produces instances of single-method interfaces which redirect calls to methods; see
36  * {@link #asInterfaceInstance(Class, Method)}.
37  * </p>
38  * <h2>Calling supplier methods with no arguments</h2>
39  * <p>
40  * If the interface's single-method defines no arguments, use {@link #asFunction(Method)} and then apply the function
41  * passing in the object to receive the method call.
42  * </p>
43  * <p>
44  * For example to invoke {@link String#length()}:
45  * </p>
46  *
47  * <pre>
48  * final Method method = String.class.getMethod("length");
49  * final Function&lt;String, Integer&gt; function = MethodInvokers.asFunction(method);
50  * assertEquals(3, function.apply("ABC"));
51  * </pre>
52  *
53  * <h2>Calling function methods with one argument</h2>
54  * <p>
55  * If the interface's single-method defines one argument, use {@link #asBiFunction(Method)} and then apply the function
56  * passing in the object to receive the method call. The second argument to the function is the only argument to the
57  * method.
58  * </p>
59  * <p>
60  * For example to invoke {@link String#charAt(int)}:
61  * </p>
62  *
63  * <pre>
64  * final Method method = String.class.getMethod("charAt", int.class);
65  * final BiFunction&lt;String, Integer, Character&gt; function = MethodInvokers.asBiFunction(method);
66  * assertEquals('C', function.apply("ABC", 2));
67  * </pre>
68  *
69  * @since 3.13.0
70  */
71 public final class MethodInvokers {
72 
73     /**
74      * Produces a {@link BiConsumer} for a given <em>consumer</em> Method. For example, a classic setter method (as opposed
75      * to a fluent setter). You call the BiConsumer with two arguments: (1) the object receiving the method call, and (2)
76      * the method argument.
77      *
78      * @param <T> the type of the first argument to the operation: The type containing the Method.
79      * @param <U> the type of the second argument to the operation: The type of the method argument.
80      * @param method the method to invoke.
81      * @return a correctly-typed wrapper for the given target.
82      */
83     @SuppressWarnings("unchecked")
asBiConsumer(final Method method)84     public static <T, U> BiConsumer<T, U> asBiConsumer(final Method method) {
85         return asInterfaceInstance(BiConsumer.class, method);
86     }
87 
88     /**
89      * Produces a {@link BiFunction} for a given a <em>function</em> Method. You call the BiFunction with two arguments: (1)
90      * the object receiving the method call, and (2) the method argument. The BiFunction return type must match the method's
91      * return type.
92      * <p>
93      * For example to invoke {@link String#charAt(int)}:
94      * </p>
95      *
96      * <pre>
97      * final Method method = String.class.getMethod("charAt", int.class);
98      * final BiFunction&lt;String, Integer, Character&gt; function = MethodInvokers.asBiFunction(method);
99      * assertEquals('C', function.apply("ABC", 2));
100      * </pre>
101      *
102      * @param <T> the type of the first argument to the function: The type containing the method.
103      * @param <U> the type of the second argument to the function: the method argument type.
104      * @param <R> the type of the result of the function: The method return type.
105      * @param method the method to invoke.
106      * @return a correctly-typed wrapper for the given target.
107      */
108     @SuppressWarnings("unchecked")
asBiFunction(final Method method)109     public static <T, U, R> BiFunction<T, U, R> asBiFunction(final Method method) {
110         return asInterfaceInstance(BiFunction.class, method);
111     }
112 
113     /**
114      * Produces a {@link FailableBiConsumer} for a given <em>consumer</em> Method. For example, a classic setter method (as
115      * opposed to a fluent setter). You call the FailableBiConsumer with two arguments: (1) the object receiving the method
116      * call, and (2) the method argument.
117      *
118      * @param <T> the type of the first argument to the operation: The type containing the Method.
119      * @param <U> the type of the second argument to the operation: The type of the method argument.
120      * @param method the method to invoke.
121      * @return a correctly-typed wrapper for the given target.
122      */
123     @SuppressWarnings("unchecked")
asFailableBiConsumer(final Method method)124     public static <T, U> FailableBiConsumer<T, U, Throwable> asFailableBiConsumer(final Method method) {
125         return asInterfaceInstance(FailableBiConsumer.class, method);
126     }
127 
128     /**
129      * Produces a {@link FailableBiFunction} for a given a <em>function</em> Method. You call the FailableBiFunction with
130      * two arguments: (1) the object receiving the method call, and (2) the method argument. The BiFunction return type must
131      * match the method's return type.
132      *
133      * @param <T> the type of the first argument to the function: The type containing the method.
134      * @param <U> the type of the second argument to the function: the method argument type.
135      * @param <R> the type of the result of the function: The method return type.
136      * @param method the method to invoke.
137      * @return a correctly-typed wrapper for the given target.
138      */
139     @SuppressWarnings("unchecked")
asFailableBiFunction(final Method method)140     public static <T, U, R> FailableBiFunction<T, U, R, Throwable> asFailableBiFunction(final Method method) {
141         return asInterfaceInstance(FailableBiFunction.class, method);
142     }
143 
144     /**
145      * Produces a {@link FailableFunction} for a given a <em>supplier</em> Method. You call the Function with one argument:
146      * the object receiving the method call. The FailableFunction return type must match the method's return type.
147      *
148      * @param <T> the type of the first argument to the function: The type containing the method.
149      * @param <R> the type of the result of the function: The method return type.
150      * @param method the method to invoke.
151      * @return a correctly-typed wrapper for the given target.
152      */
153     @SuppressWarnings("unchecked")
asFailableFunction(final Method method)154     public static <T, R> FailableFunction<T, R, Throwable> asFailableFunction(final Method method) {
155         return asInterfaceInstance(FailableFunction.class, method);
156     }
157 
158     /**
159      * Produces a {@link FailableSupplier} for a given a <em>supplier</em> Method. The FailableSupplier return type must
160      * match the method's return type.
161      * <p>
162      * Only works with static methods.
163      * </p>
164      *
165      * @param <R> The Method return type.
166      * @param method the method to invoke.
167      * @return a correctly-typed wrapper for the given target.
168      */
169     @SuppressWarnings("unchecked")
asFailableSupplier(final Method method)170     public static <R> FailableSupplier<R, Throwable> asFailableSupplier(final Method method) {
171         return asInterfaceInstance(FailableSupplier.class, method);
172     }
173 
174     /**
175      * Produces a {@link Function} for a given a <em>supplier</em> Method. You call the Function with one argument: the
176      * object receiving the method call. The Function return type must match the method's return type.
177      * <p>
178      * For example to invoke {@link String#length()}:
179      * </p>
180      *
181      * <pre>
182      * final Method method = String.class.getMethod("length");
183      * final Function&lt;String, Integer&gt; function = MethodInvokers.asFunction(method);
184      * assertEquals(3, function.apply("ABC"));
185      * </pre>
186      *
187      * @param <T> the type of the first argument to the function: The type containing the method.
188      * @param <R> the type of the result of the function: The method return type.
189      * @param method the method to invoke.
190      * @return a correctly-typed wrapper for the given target.
191      */
192     @SuppressWarnings("unchecked")
asFunction(final Method method)193     public static <T, R> Function<T, R> asFunction(final Method method) {
194         return asInterfaceInstance(Function.class, method);
195     }
196 
197     /**
198      * Produces an instance of the given single-method interface which redirects its calls to the given method.
199      * <p>
200      * For the definition of "single-method", see {@linkplain MethodHandleProxies#asInterfaceInstance(Class, MethodHandle)}.
201      * </p>
202      *
203      * @param <T> The interface type.
204      * @param interfaceClass a class object representing {@code T}.
205      * @param method the method to invoke.
206      * @return a correctly-typed wrapper for the given target.
207      * @see MethodHandleProxies#asInterfaceInstance(Class, MethodHandle)
208      */
asInterfaceInstance(final Class<T> interfaceClass, final Method method)209     public static <T> T asInterfaceInstance(final Class<T> interfaceClass, final Method method) {
210         return MethodHandleProxies.asInterfaceInstance(Objects.requireNonNull(interfaceClass, "interfaceClass"), unreflectUnchecked(method));
211     }
212 
213     /**
214      * Produces a {@link Supplier} for a given a <em>supplier</em> Method. The Supplier return type must match the method's
215      * return type.
216      * <p>
217      * Only works with static methods.
218      * </p>
219      *
220      * @param <R> The Method return type.
221      * @param method the method to invoke.
222      * @return a correctly-typed wrapper for the given target.
223      */
224     @SuppressWarnings("unchecked")
asSupplier(final Method method)225     public static <R> Supplier<R> asSupplier(final Method method) {
226         return asInterfaceInstance(Supplier.class, method);
227     }
228 
229     /**
230      * Throws NullPointerException if {@code method} is {@code null}.
231      *
232      * @param method The method to test.
233      * @return The given method.
234      * @throws NullPointerException if {@code method} is {@code null}.
235      */
requireMethod(final Method method)236     private static Method requireMethod(final Method method) {
237         return Objects.requireNonNull(method, "method");
238     }
239 
unreflect(final Method method)240     private static MethodHandle unreflect(final Method method) throws IllegalAccessException {
241         return MethodHandles.lookup().unreflect(requireMethod(method));
242     }
243 
unreflectUnchecked(final Method method)244     private static MethodHandle unreflectUnchecked(final Method method) {
245         try {
246             return unreflect(method);
247         } catch (final IllegalAccessException e) {
248             throw new UncheckedIllegalAccessException(e);
249         }
250     }
251 
252     /**
253      * No need to create instances.
254      */
MethodInvokers()255     private MethodInvokers() {
256         // noop
257     }
258 
259 }
260