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>{@code 48 * final Method method = String.class.getMethod("length"); 49 * final Function<String, Integer> 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>{@code 64 * final Method method = String.class.getMethod("charAt", int.class); 65 * final BiFunction<String, Integer, Character> 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") 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>{@code 97 * final Method method = String.class.getMethod("charAt", int.class); 98 * final BiFunction<String, Integer, Character> 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") 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") 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") 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") 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") 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>{@code 182 * final Method method = String.class.getMethod("length"); 183 * final Function<String, Integer> 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") 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 {@link 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 */ 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") 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 */ 236 private static Method requireMethod(final Method method) { 237 return Objects.requireNonNull(method, "method"); 238 } 239 240 private static MethodHandle unreflect(final Method method) throws IllegalAccessException { 241 return MethodHandles.lookup().unreflect(requireMethod(method)); 242 } 243 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 */ 255 private MethodInvokers() { 256 // noop 257 } 258 259 }