簡體   English   中英

Java 8 Lambda捕獲異常

[英]Java 8 lambda catching exception

摘自Cay Horstmann 的《不耐煩Java 8 》一書:

您是否不總是討厭必須在Runnable中處理檢查的異常? 編寫一個uncheck的方法來捕獲所有選中的異常,並將它們轉變為未選中的異常。 例如,

 new Thread(uncheck( () -> { System.out.println("Zzz"); Thread.sleep(1000); })).start(); // Look, no catch (InterruptedException)! 

提示:定義接口RunnableExrun方法可能會引發任何異常。 然后實施

 public static Runnable uncheck(RunnableEx runner) 

在uncheck函數中使用lambda表達式。

為此,我編寫如下代碼:

public interface RunnableEx {
    void run() throws Exception;
}

public class TestUncheck {
    public static Runnable uncheck(RunnableEx r) {
        return new Runnable() {
            @Override
            public void run() {
                try {
                    r.run();
                } catch (Exception e) {
                }
            }
        };
    }

    public static void main(String[] args) {
        new Thread(uncheck(
                () -> {
                    System.out.println("Zzz");
                    Thread.sleep(1000);
                }
        )).start();
    }
}

我按照提示做了嗎? 有更好的方法嗎?

另外還有一個補充問題:

為什么不能只使用Callable<Void>而不是RunnableEx?

您的代碼無法將已檢查的異常包裝為未檢查的異常。 此外,它沒有遵循使用lambda表達式的提示。 更正后的版本為:

public static Runnable uncheck(RunnableEx r) {
    return () -> {
        try {
            r.run();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    };
}

確實有可能進一步改善這一點。 由於RunnableEx接口的全部用途是包裝Runnable ,因此可以將factory方法放置在接口本身內:

public interface RunnableEx {
    void run() throws Exception;
    public static Runnable uncheck(RunnableEx r) {
        return () -> {
            try {
                r.run();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        };
    }
}

當然,現在您的調用代碼必須適用於:

public static void main(String[] args) {
    new Thread(RunnableEx.uncheck(
            () -> {
                System.out.println("Zzz");
                Thread.sleep(1000);
            }
    )).start();
}

現在,該接口本身可以實現包裝功能,從而與Runnable兼容,從而消除了使用RunnableRunnableEx實例來表示單個動作的需要:

public interface RunnableEx extends Runnable {
    void runWithException() throws Exception;
    @Override default void run() {
            try {
                runWithException();
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
    }
    public static Runnable uncheck(RunnableEx r) {
        return r;
    }
}

請注意,盡管調用代碼不會在語法上進行更改,但它會首先隱式地創建一個Runnable實例,這在創建RunnableEx實例時RunnableEx ,這就是為什么uncheck成為no-op的原因,而只是充當應創建RunnableEx的標記而非Runnable 使用此接口定義,您可以通過

public static void main(String[] args) {
    new Thread( (RunnableEx) () -> {
            System.out.println("Zzz");
            Thread.sleep(1000);
        }
    ).start();
}

在這里不能“僅使用” Callable<Void>是, Callable<Void>仍然是Callable ,要求實現代碼返回一個值。 換句話說,沒有從voidVoid隱式轉換。 因此,您可以使用它,但是lambda表達式需要return null; 然后在其末尾聲明( null是唯一與Void兼容的值)。

public class TestUncheck {
    public static Runnable uncheck(Callable<Void> r) {
        return () -> {
            try { r.call(); }
            catch (Exception e) { throw new RuntimeException(e); }
        };
    }

    public static void main(String[] args) {
        new Thread(uncheck(
                () -> {
                    System.out.println("Zzz");
                    Thread.sleep(1000);
                    return null;
                }
        )).start();
    }
}

從lambdas捕獲異常的這種方法很好用,並且是(還有一些附加功能)在流行的Java庫jOOλUnchecked類中實現的一種方法。

為了進行比較,請參見其靜態方法Unchecked.runnable ,該方法與您的解決方案相同,除了其Exception處理程序和lambda縮寫了返回值。

關於從Java 8分配給Im Patients的分配,您的uncheck方法不使用lambda(為此,請參見Unchecked.runnable方法),並且不會引發未檢查的異常。 為此,您的catch塊(當前為空)需要將異常包裝為未經檢查的異常。 有很多方法可以做到,而有關SO的許多問題都與之相關:

  1. 如何包裝檢查的異常,但將原始運行時異常保留在Java中
  2. 用運行時異常替換檢查的異常?
  3. 在Java中處理運行時異常

但是一個簡單的方法是:

throw (e instanceof RuntimeException) ? (RuntimeException) e : new RuntimeException(e);

最后,為了回答Callable<Void>問題,我假設您的意思是“而不是Runnable ”? 如果是這樣,那是因為Thread的構造函數將Runnable作為參數。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM