Skip to content

Instantly share code, notes, and snippets.

@jamesonwilliams
Last active September 1, 2020 05:35
Show Gist options
  • Save jamesonwilliams/b15b8888c2e759d575c59fb1567ea32f to your computer and use it in GitHub Desktop.
Save jamesonwilliams/b15b8888c2e759d575c59fb1567ea32f to your computer and use it in GitHub Desktop.

The goal of this exercise is to show how a simple functional interface could be used to return a Result value type. The Result value type can contain an error (getError()), or a value (getValue()), depending. A user is able to figure this out by inspecting the Result.Type, and exhausting the potential values of this enum via a switch.

Consuming a multi-values result type:

package com.amplifyframework.ioscbs;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.TextView;

public final class MainActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        DataWarehouse dataStore = new SimpleNumberWarehouse();
        dataStore.warehouse(23.4, result -> {
            switch (result.getType()) {
                case ERROR:
                    showIt(Log.getStackTraceString(result.getError()));
                    break;
                case VALUE:
                    boolean inWarehouse = dataStore.isInWarehouse(result.getValue().getSavedItem());
                    showIt("Saved! In warehouse? " + inWarehouse);
                    break;
                default:
                    break;
            }
        });
    }

    private void showIt(String aString) {
        TextView textView = findViewById(R.id.text_view);
        textView.append(aString);
    }
}

From a "data warehouse," as below. The Result and Result.Listener are the important bits.

package com.amplifyframework.ioscbs;

import java.util.Objects;

public interface DataWarehouse {
    <D> void warehouse(D data, Result.Listener<WarehousingData, WarehousingError> listener);

    <D> boolean isInWarehouse(D data);

    final class WarehousingData<T> {
        private final String message;
        private final T savedItem;

        WarehousingData(String message, T savedItem) {
            this.message = message;
            this.savedItem = savedItem;
        }

        T getSavedItem() {
            return savedItem;
        }

        @Override
        public String toString() {
            return "SaveInfo{" +
                "message='" + message + '\'' +
                ", savedItem=" + savedItem +
                '}';
        }

        @Override
        public boolean equals(Object thatObject) {
            if (this == thatObject) {
                return true;
            }
            if (thatObject == null || getClass() != thatObject.getClass()) {
                return false;
            }

            WarehousingData<?> warehousingData = (WarehousingData<?>) thatObject;

            if (!Objects.equals(message, warehousingData.message)) {
                return false;
            }
            return Objects.equals(savedItem, warehousingData.savedItem);
        }

        @Override
        public int hashCode() {
            int result = message != null ? message.hashCode() : 0;
            result = 31 * result + (savedItem != null ? savedItem.hashCode() : 0);
            return result;
        }
    }

    final class WarehousingError extends Exception {
        WarehousingError(String message) {
            super(message);
        }
    }

    final class Result<T, E extends Throwable> {
        enum Type {
            VALUE,
            ERROR
        }

        private final Type type;
        private final T value;
        private final E error;

        private Result(Type type, T value, E error) {
            this.type = type;
            this.value = value;
            this.error = error;
        }

        static <T, E extends Throwable> Result<T, E> value(T value) {
            return new Result<>(Type.VALUE, value, null);
        }

        static <T, E extends Throwable> Result<T, E> error(E error) {
            return new Result<>(Type.ERROR, null, error);
        }

        Type getType() {
            return type;
        }

        T getValue() {
            return value;
        }

        E getError() {
            return error;
        }

        @Override
        public boolean equals(Object o) {
            if (this == o) return true;
            if (o == null || getClass() != o.getClass()) return false;

            Result<?, ?> that = (Result<?, ?>) o;

            if (type != that.type) return false;
            if (!Objects.equals(value, that.value)) {
                return false;
            }
            return Objects.equals(error, that.error);
        }

        @Override
        public int hashCode() {
            int result = type != null ? type.hashCode() : 0;
            result = 31 * result + (value != null ? value.hashCode() : 0);
            result = 31 * result + (error != null ? error.hashCode() : 0);
            return result;
        }

        @Override
        public String toString() {
            return "ValueResult{" +
                "type=" + type +
                ", value=" + value +
                ", error=" + error +
                '}';
        }

        public interface Listener<T, E extends Throwable> {
            void accept(Result<T, E> value);
        }
    }
}

Some stupid implementation which only warehouses numbers, in memory, for the sake of the exercise.

package com.amplifyframework.ioscbs;

import java.util.ArrayList;
import java.util.List;

final class SimpleNumberWarehouse implements DataWarehouse {
    private final List<Number> numbers;

    SimpleNumberWarehouse() {
        this.numbers = new ArrayList<>();
    }

    @Override
    public <D> void warehouse(D value, Result.Listener<WarehousingData, WarehousingError> listener) {
        if (Number.class.isAssignableFrom(value.getClass())) {
            numbers.add((Number) value);
            listener.accept(Result.value(new WarehousingData<>("Okie dokie.", value)));
        } else {
            listener.accept(Result.error(new WarehousingError("Only numbers allowed!")));
        }
    }

    @Override
    public <D> boolean isInWarehouse(D data) {
        if (Number.class.isAssignableFrom(data.getClass())) {
            Number number = (Number) data;
            return numbers.contains(number);
        }
        return false;
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment