在Java8的Consumer中抛出异常

最近在实现公司内部一个通用svn数据工具类,其中有段代码是拿到当前更新后的数据进行下一步操作,用的是java8的Consumer实现的,代码如下:

1
public void save(final DTO dto, final Consumer<List<T>> cons) throws Exception

这段代码一开始并没有什么问题,但是投入生产后发现有些异常数据导致服务器报错了,但是前台还是返回操作成功,debug查看后发现是异常被调用方吃掉了,原因了原生Consumer不支持异常抛出,只能内部处理,接到反馈后,自己测试确实能复现,查看Consumer源码发现原生确实不支持抛出,查阅网络资料,发现只能重写一个Consumer方法,特此记录一下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
    @FunctionalInterface
    public interface Consumer<T> {
    
        /**
         * Performs this operation on the given argument.
         *
         * @param t the input argument
         */
        void accept(T t);//并没有异常处理
    
        /**
         * Returns a composed {@code Consumer} that performs, in sequence, this
         * operation followed by the {@code after} operation. If performing either
         * operation throws an exception, it is relayed to the caller of the
         * composed operation.  If performing this operation throws an exception,
         * the {@code after} operation will not be performed.
         *
         * @param after the operation to perform after this operation
         * @return a composed {@code Consumer} that performs in sequence this
         * operation followed by the {@code after} operation
         * @throws NullPointerException if {@code after} is null
         */
        default Consumer<T> andThen(Consumer<? super T> after) {
            Objects.requireNonNull(after);
            return (T t) -> { accept(t); after.accept(t); };
        }
    }

解决方法:

  1. 新建一个ThrowingConsumer.class文件
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    
    import java.util.function.Consumer;
        
    /**
     * @ClassName: ThrowingConsumer
     * @Description: 重写Java8的Consumer中的异常抛出
     * @author:Erwin.Zhang
     * @date: 2021-03-01 10:59:19
     */
    @FunctionalInterface
    public interface ThrowingConsumer<T> extends Consumer<T> {
        
        @Override
        default void accept(final T e) {
            try {
                accept0(e);
            } catch (Throwable ex) {
                Throwing.sneakyThrow(ex);
            }
        }
        
        void accept0(T e) throws Throwable;
        
    }
    
  2. 新建一个处理异常的Throwing.class
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    import java.util.function.Consumer;
        
    import javax.validation.constraints.NotNull;
        
    /**
     * @ClassName: Throwing
     * @Description: 在Java8的Consumer中抛出异常
     * @author:Erwin.Zhang
     * @date: 2021-03-01 10:58:31
     */
    public class Throwing {
        private Throwing() {}
        
        @NotNull
        public static <T> Consumer<T> rethrow(@NotNull final ThrowingConsumer<T> consumer) {
            return consumer;
        }
        
        /**
         * The compiler sees the signature with the throws T inferred to a RuntimeException type, so it allows the unchecked
         * exception to propagate.
         *
         * http://www.baeldung.com/java-sneaky-throws
         */
        @SuppressWarnings("unchecked")
        @NotNull
        public static <E extends Throwable> void sneakyThrow(@NotNull final Throwable ex) throws E {
            throw (E)ex;
        }
    }
    
  3. 使用方法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    import java.io.IOException;
    import java.util.Arrays;
        
    import org.junit.Rule;
    import org.junit.Test;
    import org.junit.rules.ExpectedException;
        
    /**
     * @ClassName: ThrowingTest
     * @Description: 测试异常抛出
     * @author:Erwin.Zhang
     * @date: 2021-03-01 11:00:38
     */
    public class ThrowingTest {
        @Rule
        public ExpectedException thrown = ExpectedException.none();
        
        @Test
        public void testRethrow() {
            thrown.expect(IOException.class);
            thrown.expectMessage("i=3");
        
            Arrays.asList(1, 2, 3).forEach(rethrow(e -> {
                int i = e;
                if (i == 3) {
                    throw new IOException("i=" + i);
                }
            }));
        }
        
        @Test(expected = IOException.class)
        public void testSneakyThrow() {
            Throwing.sneakyThrow(new IOException());
        }
        
        @Test
        public void testThrowingConsumer() {
            thrown.expect(IOException.class);
            thrown.expectMessage("i=3");
        
            Arrays.asList(1, 2, 3).forEach((ThrowingConsumer<Integer>)e -> {
                int i = e;
                if (i == 3) {
                    throw new IOException("i=" + i);
                }
            });
        }
        
    }
    
坚持原创技术分享,您的支持将鼓励我继续创作!