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 */ 017package org.apache.juneau.commons.function; 018 019import static org.apache.juneau.commons.utils.AssertionUtils.*; 020import static org.apache.juneau.commons.utils.ThrowableUtils.*; 021 022/** 023 * A functional interface representing an operation that accepts two arguments, returns no result, and may throw a checked exception. 024 * 025 * <p> 026 * This interface extends {@link Consumer2} to allow the functional method 027 * to throw checked exceptions. The default {@link #apply(Object, Object)} method wraps any checked exceptions in a 028 * {@link RuntimeException}, making it compatible with standard {@link Consumer2} usage while still allowing 029 * the implementation to throw checked exceptions via {@link #acceptThrows(Object, Object)}. 030 * 031 * <h5 class='section'>Features:</h5> 032 * <ul class='spaced-list'> 033 * <li>Functional interface - can be used with lambda expressions and method references 034 * <li>Exception support - allows checked exceptions to be thrown via {@link #acceptThrows(Object, Object)} 035 * <li>Compatible with Consumer2 - implements {@link Consumer2#apply(Object, Object)} by wrapping exceptions 036 * <li>Dual interface - can be used as both Consumer2 and ThrowingConsumer2 037 * </ul> 038 * 039 * <h5 class='section'>Use Cases:</h5> 040 * <ul class='spaced-list'> 041 * <li>Two-argument operations that may throw I/O exceptions 042 * <li>Validation operations that may throw validation exceptions 043 * <li>Side-effect operations that may fail with checked exceptions 044 * <li>Operations that need to propagate checked exceptions 045 * </ul> 046 * 047 * <h5 class='section'>Usage:</h5> 048 * <p class='bjava'> 049 * <jc>// Lambda with exception</jc> 050 * ThrowingConsumer2<String,Integer> <jv>fileWriter</jv> = (<jv>path</jv>, <jv>content</jv>) -> { 051 * Files.write(Paths.get(<jv>path</jv>), <jv>content</jv>.getBytes()); 052 * }; 053 * 054 * <jc>// Using acceptThrows to get checked exception</jc> 055 * <jk>try</jk> { 056 * <jv>fileWriter</jv>.acceptThrows(<js>"/tmp/output.txt"</js>, <js>"Hello World"</js>); 057 * } <jk>catch</jk> (IOException <jv>e</jv>) { 058 * <jc>// Handle checked exception</jc> 059 * } 060 * 061 * <jc>// Using apply wraps exception in RuntimeException</jc> 062 * <jv>fileWriter</jv>.apply(<js>"/tmp/output.txt"</js>, <js>"Hello World"</js>); <jc>// May throw RuntimeException</jc> 063 * </p> 064 * 065 * <h5 class='section'>Exception Handling:</h5> 066 * <ul class='spaced-list'> 067 * <li>{@link #acceptThrows(Object, Object)} - Throws checked exceptions directly 068 * <li>{@link #apply(Object, Object)} - Wraps checked exceptions in {@link RuntimeException} 069 * <li>Use {@code acceptThrows} when you need to handle specific checked exceptions 070 * <li>Use {@code apply} when you want standard Consumer2 behavior 071 * </ul> 072 * 073 * <h5 class='section'>See Also:</h5><ul> 074 * <li class='jc'>{@link ThrowingConsumer} - Single-argument consumer that throws exceptions 075 * <li class='jc'>{@link ThrowingConsumer3} - Three-argument consumer that throws exceptions 076 * <li class='jc'>{@link ThrowingConsumer4} - Four-argument consumer that throws exceptions 077 * <li class='jc'>{@link ThrowingConsumer5} - Five-argument consumer that throws exceptions 078 * <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/JuneauCommonsBasics">juneau-commons Basics</a> 079 * </ul> 080 * 081 * @param <A> The type of the first argument to the operation. 082 * @param <B> The type of the second argument to the operation. 083 */ 084@FunctionalInterface 085public interface ThrowingConsumer2<A,B> extends Consumer2<A,B> { 086 087 /** 088 * Performs this operation on the given arguments, wrapping any checked exceptions in a {@link RuntimeException}. 089 * 090 * <p> 091 * This is the default implementation that makes this interface compatible with {@link Consumer2}. 092 * It calls {@link #acceptThrows(Object, Object)} and wraps any checked exceptions. 093 * 094 * @param a The first operation argument. 095 * @param b The second operation argument. 096 * @throws RuntimeException if {@link #acceptThrows(Object, Object)} throws a checked exception. 097 */ 098 @Override 099 default void apply(A a, B b) { 100 try { 101 acceptThrows(a, b); 102 } catch (Exception e) { 103 throw toRex(e); 104 } 105 } 106 107 /** 108 * Returns a composed {@link ThrowingConsumer2} that performs, in sequence, this operation followed by the {@code after} operation. 109 * 110 * <p> 111 * This method enables operation composition, allowing you to chain multiple operations together. 112 * If performing either operation throws an exception, it is relayed to the caller of the composed operation. 113 * If performing this operation throws an exception, the {@code after} operation will not be performed. 114 * 115 * @param after The operation to perform after this operation. Must not be <jk>null</jk>. 116 * @return A composed {@link ThrowingConsumer2} that performs in sequence this operation followed by the {@code after} operation. 117 * @throws NullPointerException if {@code after} is <jk>null</jk>. 118 */ 119 default ThrowingConsumer2<A,B> andThen(ThrowingConsumer2<? super A,? super B> after) { // NOSONAR - false positive on generics 120 assertArgNotNull("after", after); 121 return (A a, B b) -> { 122 acceptThrows(a, b); 123 after.acceptThrows(a, b); 124 }; 125 } 126 127 /** 128 * Performs this operation on the given arguments, potentially throwing a checked exception. 129 * 130 * <p> 131 * This is the functional method that implementations must provide. It allows checked exceptions 132 * to be thrown directly, unlike the standard {@link Consumer2#apply(Object, Object)} method. 133 * 134 * <h5 class='section'>Example:</h5> 135 * <p class='bjava'> 136 * ThrowingConsumer2<String,Integer> <jv>validator</jv> = (<jv>name</jv>, <jv>age</jv>) -> { 137 * <jk>if</jk> (<jv>name</jv> == <jk>null</jk> || <jv>name</jv>.isEmpty()) { 138 * <jk>throw new</jk> ValidationException(<js>"Name cannot be empty"</js>); 139 * } 140 * <jk>if</jk> (<jv>age</jv> < 0) { 141 * <jk>throw new</jk> ValidationException(<js>"Age cannot be negative"</js>); 142 * } 143 * }; 144 * 145 * <jk>try</jk> { 146 * <jv>validator</jv>.acceptThrows(<js>""</js>, -1); 147 * } <jk>catch</jk> (ValidationException <jv>e</jv>) { 148 * <jc>// Handle checked exception</jc> 149 * } 150 * </p> 151 * 152 * @param a The first operation argument. 153 * @param b The second operation argument. 154 * @throws Exception If an error occurs during operation execution. 155 */ 156 void acceptThrows(A a, B b) throws Exception; 157} 158