Last active
June 28, 2019 11:09
-
-
Save will-molloy/53138688693a28f806c99c36ebe82d12 to your computer and use it in GitHub Desktop.
Java sealed class (abstract enum)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Optional; | |
import java.util.function.Supplier; | |
import java.util.stream.Stream; | |
/** | |
* Example 1. __Unsealed | |
* | |
* <p>Can implement the root abstraction, but we want to restrict the subtypes to provide rigidity | |
* and exhaustiveness of the type. | |
*/ | |
public abstract class __Unsealed<T> implements Supplier<T> { | |
abstract String getName(); | |
public abstract static class Multiple extends __Unsealed<Stream<Integer>> {} | |
public abstract static class Single extends __Unsealed<Optional<Integer>> { | |
public final Multiple asMultiple() { | |
return new Multiple() { | |
@Override | |
public Stream<Integer> get() { | |
return Single.this.get().map(Stream::of).orElse(Stream.empty()); | |
} | |
@Override | |
public String getName() { | |
return Single.this.getName(); | |
} | |
}; | |
} | |
} | |
} | |
public class MultipleInstance extends __Unsealed.Multiple { | |
@Override | |
public String getName() { | |
return "I have 0 or more integer"; | |
} | |
@Override | |
public Stream<Integer> get() { | |
return Stream.of(1); | |
} | |
} | |
public class SingleInstance extends __Unsealed.Single { | |
@Override | |
public String getName() { | |
return "I have 0 or 1 integer"; | |
} | |
@Override | |
public Optional<Integer> get() { | |
return Optional.of(1); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Optional; | |
import java.util.function.Supplier; | |
import java.util.stream.Stream; | |
/** | |
* Example 2. No common inheritance hierarchy. | |
* | |
* <p>Multiple and Single have no common root. So there is no common root to extend! | |
* | |
* <p>Gives what we want but not ideal, the {@code getName()} method is now duplicated! | |
*/ | |
public abstract class _NoCommonRoot { | |
private _NoCommonRoot() {} | |
public abstract static class Multiple implements Supplier<Stream<Integer>> { | |
protected abstract String getName(); | |
} | |
public abstract static class Single implements Supplier<Optional<Integer>> { | |
protected abstract String getName(); | |
public final Multiple asMultiple() { | |
return new Multiple() { | |
@Override | |
public Stream<Integer> get() { | |
return Single.this.get().stream(); | |
} | |
@Override | |
public String getName() { | |
return Single.this.getName(); | |
} | |
}; | |
} | |
} | |
} | |
public class MultipleInstance extends _NoCommonRoot.Multiple { | |
@Override | |
public String getName() { | |
return "I have 0 or more integer"; | |
} | |
@Override | |
public Stream<Integer> get() { | |
return Stream.of(1); | |
} | |
} | |
public class SingleInstance extends _NoCommonRoot.Single { | |
@Override | |
public String getName() { | |
return "I have 0 or 1 integer"; | |
} | |
@Override | |
public Optional<Integer> get() { | |
return Optional.of(1); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import java.util.Optional; | |
import java.util.function.Supplier; | |
import java.util.stream.Stream; | |
/** | |
* Example 3. Sealed | |
* | |
* <p>Now sealed. Outsiders cannot implement the root abstraction. | |
* | |
* <p>(i.e. {@code extends Sealed} is forbidden). | |
*/ | |
public abstract class Sealed<T> implements Supplier<T> { | |
// prevent extension via. private constructor | |
// effectively 'seals' the class like in Scala, Kotlin etc. | |
private Sealed() {} | |
protected abstract String getName(); | |
public abstract static class Multiple extends Sealed<Stream<Integer>> {} | |
public abstract static class Single extends Sealed<Optional<Integer>> { | |
public final Multiple asMultiple() { | |
return new Multiple() { | |
@Override | |
public Stream<Integer> get() { | |
return Single.this.get().stream(); | |
} | |
@Override | |
public String getName() { | |
return Single.this.getName(); | |
} | |
}; | |
} | |
} | |
} | |
public class MultipleInstance extends Sealed.Multiple { | |
@Override | |
public String getName() { | |
return "I have 0 or more integer"; | |
} | |
@Override | |
public Stream<Integer> get() { | |
return Stream.of(1); | |
} | |
} | |
public class SingleInstance extends Sealed.Single { | |
@Override | |
public String getName() { | |
return "I have 0 or 1 integer"; | |
} | |
@Override | |
public Optional<Integer> get() { | |
return Optional.of(1); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Note the final solution does not provide exhaustiveness of the type (i.e. Sealed), will have to wait for sealed interfaces for that!
https://openjdk.java.net/jeps/8222777