Приемлема ли эта непроверенная оболочка, учитывая, что проверенная ошибка не будет выбрана в моей программе - PullRequest
0 голосов
/ 28 мая 2020

Я использую реализацию Consumer Functional Interface, которая обертывает лямбда-выражение и, по-видимому, улавливает любое проверенное исключение и вместо этого выдает непроверенное исключение среды выполнения. Мне это очень нравится, потому что он очищает мои лямбды, и я использую его в ситуации, когда проверенная ошибка в этом случае InterruptedException никогда не будет сгенерирована. Это приемлемо, и если нет, то почему? Ниже приводится реализация интерфейса:

@FunctionalInterface
public interface ThrowingConsumer<T, E extends Throwable> {

void accept(T t) throws E;

static <T, E extends Throwable> Consumer<T> unchecked(ThrowingConsumer<T, E> c){
    return t -> {
        try{
            c.accept(t);
        } catch (Throwable e){
            throw new RuntimeException();
        }
    };
}

}

Ниже приводится один пример его использования. Чтобы дать контекст, он берет EntrySet из ConcurrentHashMap, который содержит для ключа идентификатор пользователя и значение Optional BlockingArrayQueue типа Packet, используемое в качестве средства связи с каждым соответствующим пользователем. В этом сценарии я отправляю каждому пользователю пакет, информируя его о том, что сервер ретрансляции вскоре отключится, чтобы дать им возможность полностью закрыть, прежде чем их потоки будут прерваны. Поскольку поток, который их породил, был Main и с тех пор завершил выполнение, а прерывание пришло из ShutdownHook, по-видимому, не может произойти другое прерывание.

public class NetworkThread implements Runnable {

private SecureSocketManager secureSocketManager;
private final int tlsPort;
private final ConcurrentHashMap<String, Optional<BlockingQueue<Packet>>> channelMap;


public NetworkThread(SecureSocketManager secureSocketManager, ConcurrentHashMap<String, Optional<BlockingQueue<Packet>>> channelMap, int tlsPort) {
    this.secureSocketManager = secureSocketManager;
    this.tlsPort = tlsPort;
    this.channelMap = channelMap;
}

public void run() {

    ExecutorService clientThreads = Executors.newCachedThreadPool();
    // SETUP A SECURESOCKETMANAGER INSTANCE AND ESTABLISH A SERVER SOCKET ON DESIRED PORT

    try {
        SSLServerSocket sslServerSocket = secureSocketManager.getSslServerSocket(tlsPort);

        // BEGIN RECEIVING NEW CONNECTION INSTANCES ON THAT PORT AND LOOP

        while (!interrupted()) {

            try {
                SSLSocket sslSocket = (SSLSocket) sslServerSocket.accept();
                clientThreads.execute(new ClientThread(sslSocket, channelMap));

            } catch (IOException e) {
                e.printStackTrace();
            }
        }

//------------------------------RELEVANT CODE----------------------------------------------
    clientThreads.shutdown();

        // There should be no second interrupt so having this unchecked lambda wrapper is acceptable
       channelMap.entrySet()
               .stream()
               .filter(e -> e.getValue().isPresent())
               .forEach(ThrowingConsumer.unchecked(e -> e.getValue().get() // WRAPPER USE
               .put(new Packet(e.getKey(), "",Type.RELAY_SHUTDOWN, "", "" )))); 
       if(!clientThreads.awaitTermination(3, TimeUnit.MINUTES))
           clientThreads.shutdownNow();

//------------------------------------------------------------------------------------------

    } catch (IOException | InterruptedException e) {
        clientThreads.shutdownNow();
    }


}

}

Спасибо

1 Ответ

0 голосов
/ 28 мая 2020

Приемлемо ли это ...

Спросите человека, который должен это принять. (Или решайте сами!)

... а если нет, почему?

Очень жаль, если вам приходится иметь дело с проверенными исключениями в потоке. Но если вам нужно, то вам придется.

Но у меня есть пара проблем с тем, как вы это делаете:

  1. Вы перехватываете все исключения, а не только что проверил исключения. Если бы вы просто поймали Exception, это было бы плохо. Но вы также поймаете Error.

  2. Вы должны обернуть исходное исключение. В настоящее время вы его выбрасываете.

  3. Для исключения вашей оболочки требуется сообщение.


Я думаю, что это касается выше проблемы, а также возможно:

@FunctionalInterface
public interface ThrowingConsumer<T, E extends Exception> {

    void accept(T t) throws E;

    static <TT, EE extends Exception> Consumer<TT> unchecked(ThrowingConsumer<TT, EE> c){
        return t -> {
            try {
                c.accept(t);
            } catch (Exception e) {
                if (e instanceof RuntimeException) {
                    throw e;
                } else {
                    throw new RuntimeException("Unexpected exception", e);
                }
            }
        };
    }
}

Даже это заставляет меня чувствовать себя немного неловко, потому что вы полагаетесь на безопасность типов generi c, чтобы гарантировать, что вы поймаете / обернете только специфицированные c проверил исключение и его подклассы. Проблема в том, что если вы используете неконтролируемые преобразования и т.д. c, вы можете нарушить общую безопасность типов c. В этом случае это может привести к тому, что "неправильное" проверенное исключение будет перехвачено и упаковано. Это нормально ... если вы не попытаетесь развернуть его и привести к ожидаемому типу.

В идеале вы должны использовать время выполнения проверку типов, чтобы гарантировать, что вы оберните только ожидаемый исключение. Но для этого необходимо передать проверенный объект исключения Class методу unchecked.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...