001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018package org.apache.commons.lang3.function; 019 020import java.lang.invoke.MethodHandle; 021import java.lang.invoke.MethodHandleProxies; 022import java.lang.invoke.MethodHandles; 023import java.lang.reflect.Method; 024import java.util.Objects; 025import java.util.function.BiConsumer; 026import java.util.function.BiFunction; 027import java.util.function.Function; 028import java.util.function.Supplier; 029 030import org.apache.commons.lang3.exception.UncheckedIllegalAccessException; 031 032/** 033 * Converts {@link Method} objects to lambdas. 034 * <p> 035 * More specifically, produces instances of single-method interfaces which redirect calls to methods; see 036 * {@link #asInterfaceInstance(Class, Method)}. 037 * </p> 038 * <h2>Calling supplier methods with no arguments</h2> 039 * <p> 040 * If the interface's single-method defines no arguments, use {@link #asFunction(Method)} and then apply the function 041 * passing in the object to receive the method call. 042 * </p> 043 * <p> 044 * For example to invoke {@link String#length()}: 045 * </p> 046 * 047 * <pre>{@code 048 * final Method method = String.class.getMethod("length"); 049 * final Function<String, Integer> function = MethodInvokers.asFunction(method); 050 * assertEquals(3, function.apply("ABC")); 051 * }</pre> 052 * 053 * <h2>Calling function methods with one argument</h2> 054 * <p> 055 * If the interface's single-method defines one argument, use {@link #asBiFunction(Method)} and then apply the function 056 * passing in the object to receive the method call. The second argument to the function is the only argument to the 057 * method. 058 * </p> 059 * <p> 060 * For example to invoke {@link String#charAt(int)}: 061 * </p> 062 * 063 * <pre>{@code 064 * final Method method = String.class.getMethod("charAt", int.class); 065 * final BiFunction<String, Integer, Character> function = MethodInvokers.asBiFunction(method); 066 * assertEquals('C', function.apply("ABC", 2)); 067 * }</pre> 068 * 069 * @since 3.13.0 070 */ 071public final class MethodInvokers { 072 073 /** 074 * Produces a {@link BiConsumer} for a given <em>consumer</em> Method. For example, a classic setter method (as opposed 075 * to a fluent setter). You call the BiConsumer with two arguments: (1) the object receiving the method call, and (2) 076 * the method argument. 077 * 078 * @param <T> the type of the first argument to the operation: The type containing the Method. 079 * @param <U> the type of the second argument to the operation: The type of the method argument. 080 * @param method the method to invoke. 081 * @return a correctly-typed wrapper for the given target. 082 */ 083 @SuppressWarnings("unchecked") 084 public static <T, U> BiConsumer<T, U> asBiConsumer(final Method method) { 085 return asInterfaceInstance(BiConsumer.class, method); 086 } 087 088 /** 089 * Produces a {@link BiFunction} for a given a <em>function</em> Method. You call the BiFunction with two arguments: (1) 090 * the object receiving the method call, and (2) the method argument. The BiFunction return type must match the method's 091 * return type. 092 * <p> 093 * For example to invoke {@link String#charAt(int)}: 094 * </p> 095 * 096 * <pre>{@code 097 * final Method method = String.class.getMethod("charAt", int.class); 098 * final BiFunction<String, Integer, Character> function = MethodInvokers.asBiFunction(method); 099 * 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}