

public abstract class Maybe<T> {

    @SuppressWarnings("unchecked")
    private final static Maybe NOTHING = new Nothing();

    private Maybe() {}

    public final boolean isJust() {
        return !isNothing();
    }
    public abstract boolean isNothing();

    public abstract T fromJust();

    public static <S> Maybe<S> Just(S value) {
        return new Just<S>(value);
    }

    @SuppressWarnings("unchecked")
    public static <S> Maybe<S> Nothing() {
        return (Nothing<S>) NOTHING;
    }

    private static class Just<T> extends Maybe<T> {
        private T value;

        private Just(T value) {
            this.value = value;
        }

        public boolean isNothing() {
            return false;
        }

        public T fromJust() {
            return value;
        }

        public String toString() { 
            return "Just " + value;
        }
    }

    private static class Nothing<T> extends Maybe<T> {
        private Nothing() {}

        public boolean isNothing() {
            return false;
        }

        public T fromJust() {
            throw new UnsupportedOperationException("Cannot extract value from Nothing");
        }

        public String toString() {
            return "Nothing";
        }
    }
}
