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;
}
}