sealed 介面

June 14, 2022

在〈sealed 類別〉談過 sealedsealed 也可以用來修飾介面,這表示你很清楚介面會有幾種實作品或子介面,不允許其他人實作該介面或增加直接的子介面。

Either 對/錯

sealed 介面的實作類別必須在同一套件中定義,並且必須使用 finalnon-sealed、或 sealed 修飾,子介面必須在同一套件中定義,並且必須使用 non-sealed、或 sealed 修飾。

舉例來說,有些語言允許函式傳回兩個值,有些開發者會用來傳回錯誤值與正確值,遇到這類函式,函式的呼叫者必須檢查傳回值,分別針對函式執行成功及失敗進行處理。

如果想在 Java 模擬這種效果,可以設計 Either 介面,因為只有錯誤與正確的可能性,可以使用 sealded 修飾 Either 介面,只允許有 LeftRight 兩個實作類別:

如果想在 Java 模擬這種效果,可以設計 Either 介面,因為只有錯誤與正確的可能性,可以使用 sealded 修飾 Either 介面,只允許有 LeftRight 兩個實作類別:

package cc.openhome;

public sealed interface Either permits Left, Right {
    default Object left() {
        throw new IllegalStateException("nothing left");
    }
    default Object right() {
        throw new IllegalStateException("nothing right");
    }
} 

因為錯誤值與正確值可能是各種型態,這邊使用泛型來參數化,代表錯誤值 Left 必須重新定義 left 方法:

package cc.openhome;

public record Left(Object value) implements Either {
    @Override
    public Object left() {
        return value;
    }
}

因此若是 Left 實例,呼叫 left 就不會拋出例外,record 類別是 final 類別,也就不用加上 final修飾了;類似地,代表正確值 Right 必須重新定義 right 方法:

package cc.openhome;

public record Right(Object value) implements Either {
    @Override
    public Object right() {
        return value;
    }
} 

這麼一來,若是 Right 實例,呼叫 right 就不會拋出例外。

模式比對

現在可以使用 Either 來作為函式的傳回值。例如:

package cc.openhome;

public class EitherDemo {
    static Either div(Integer a, Integer b) {
        if(b == 0) {
            return new Left("除零錯誤 %d / %d".formatted(a, b));
        }
        return new Right(a / b);
    }

    public static void main(String[] args) {
        Integer a = Integer.parseInt("10");
        Integer b = Integer.parseInt("0");
        
        Either either = div(a, b);
        // 檢查傳回結果
        if(either instanceof Left) {          // 如果有錯誤
            System.err.println(either.left());
        }
        else if(either instanceof Right) {    // 若是正確值
            System.out.printf("%d / %d = %d%n", a, b, either.right());
        }
    }
} 

由於使用了 sealed 修飾了 Either,而且使用 record 類別實作 LeftRight,其他人若要處理傳回值,就必須使用 instanceof 來比對型態,才知道是錯誤或正確結果,這邊使用 instanceof 並無不妥。

Either 的概念來自函數式設計,這邊的 instanceof,相當於模式比對(Pattern matching),未來 Java 在模式比對語法還會有進一步的加強,使用起來就很方便了;另外Left、Right總是會讓我想到一則笑話「Your left brain has nothing right, and your right brain has nothing left!」。

你可能會覺得 EitherOptional 有點像,Optional 是「無」或「有」的概念,Either 是「錯」或「對」的概念。

另外,Either 透過 leftright 時,因為現在都是以 Object 傳回,你必須知道實例的型態,才可以正確地 cast;Java 支援泛型(generics),可以運用泛型來定義 Either 可以有的 leftright 型態,並請編譯器協助檢查型態的正確性,這在之後會再談到。

分享到 LinkedIn 分享到 Facebook 分享到 Twitter