Skip to content

Instantly share code, notes, and snippets.

@joseronierison
Last active July 20, 2018 18:18
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joseronierison/638203d069c0230163096f6e3ea43d4f to your computer and use it in GitHub Desktop.
Save joseronierison/638203d069c0230163096f6e3ea43d4f to your computer and use it in GitHub Desktop.
Try Monad in Java (WIP)
package utils;
import lombok.EqualsAndHashCode;
import java.util.Objects;
import java.util.concurrent.Callable;
import java.util.function.Function;
@EqualsAndHashCode
public abstract class Try<V> {
private V value;
public abstract Boolean isSuccess();
public abstract Boolean isFailure();
public static <V> Try<V> failure(Exception e) {
return new Failure<>(e);
}
public static <V> Try<V> success(V value) {
return new Success<>(value);
}
public static <V> Try<V> run(Callable<V> riskOperation) {
try {
return new Success<>(riskOperation.call());
} catch (Throwable exception) {
return new Failure<>(exception);
}
}
public V get() {
if(value == null) {
throw new IllegalStateException("Operation returned failure and does not have a value");
}
return value;
}
public V orElse(V defaultValue) {
return value == null ? defaultValue : value;
}
public <U> Try<U> flatMap(Function<? super V, Try<U>> mapper) {
Objects.requireNonNull(mapper);
if (isFailure())
return (Try<U>)this;
else {
return Objects.requireNonNull(mapper.apply(value));
}
}
public <U> Try<U> map(Function<? super V, U> mapper) {
Objects.requireNonNull(mapper);
if (isFailure())
return (Try<U>)this;
else {
return success(mapper.apply(value));
}
}
@EqualsAndHashCode(callSuper = false)
private static class Failure<V> extends Try<V> {
private String message;
Failure(Throwable e) {
super();
this.message = e.getMessage();
}
@Override
public Boolean isSuccess() {
return false;
}
@Override
public Boolean isFailure() {
return true;
}
@Override
public String toString() {
return "Failure(" + message + ")";
}
}
@EqualsAndHashCode(callSuper = false)
private static class Success<V> extends Try<V> {
Success(V value) {
super();
super.value = value;
}
@Override
public Boolean isSuccess() {
return true;
}
@Override
public Boolean isFailure() {
return false;
}
@Override
public String toString() {
return "Success(" + super.value.toString() + ")";
}
}
}
package utils;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import java.util.function.Function;
import static org.hamcrest.core.Is.is;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.*;
@RunWith(MockitoJUnitRunner.class)
public class TryTest {
@Mock
private Function<? super String, Try<Integer>> flatMapMockCallable;
@Mock
private Function<? super String, Integer> mapMockCallable;
@Test
public void runShouldReturnSuccessWhenCallDoesNotThrowAnException() {
Try<String> expectedOperationResult = Try.success("everything goes well");
Try<String> actualOperationResult = Try.run(() -> "everything goes well");
assertThat(actualOperationResult, is(expectedOperationResult));
}
@Test
public void runShouldReturnFailureWhenCallDoesThrowAnException() {
Exception expectedException = new Exception("everything goes bad");
Try<String> expectedOperationResult = Try.failure(expectedException);
Try<String> actualOperationResult = Try.run(() -> { throw expectedException; });
assertThat(actualOperationResult, is(expectedOperationResult));
}
@Test
public void isSuccessShouldReturnTrueWhenTestHasSuccess() {
Try<String> actualOperationResult = Try.run(() -> "everything goes well");
assertThat(actualOperationResult.isSuccess(), is(true));
}
@Test
public void isSuccessShouldReturnFalseWhenTestFails() {
Try<String> actualOperationResult = Try.run(() -> { throw new Exception("everything goes bad"); });
assertThat(actualOperationResult.isSuccess(), is(false));
}
@Test
public void isFailureShouldReturnFalseWhenTestHasSuccess() {
Try<String> actualOperationResult = Try.run(() -> "everything goes well");
assertThat(actualOperationResult.isFailure(), is(false));
}
@Test
public void isFailureShouldReturnTrueWhenTestFails() {
Try<String> actualOperationResult = Try.run(() -> { throw new Exception("everything goes bad"); });
assertThat(actualOperationResult.isFailure(), is(true));
}
@Test
public void getShouldReturnValueWhenThereIsntAnException() {
String expectedValue = "everything goes well";
Try<String> actualOperationResult = Try.run(() -> expectedValue);
assertThat(actualOperationResult.get(), is(expectedValue));
}
@Test(expected = IllegalStateException.class)
public void getShouldThrowIllegalStateExceptionWhenThereIsAnException() {
Try<Object> riskOperationResult = Try.run(() -> {
throw new Exception("everything goes bad");
});
riskOperationResult.get();
}
@Test
public void orElseShouldReturnProvidedValueWhenEveryThingGoesWell() {
String expectedValue = "everything goes well";
Try<String> actualOperationResult = Try.run(() -> expectedValue);
assertThat(actualOperationResult.orElse("other value"), is(expectedValue));
}
@Test
public void orElseShouldReturnDefaultValueWhenAnExceptionIsThrown() {
Try<String> actualOperationResult = Try.run(() -> { throw new Exception("everything goes bad"); });
String defaultValue = "other value";
assertThat(actualOperationResult.orElse(defaultValue), is(defaultValue));
}
@Test
public void flatMapShouldReturnItSelfWhenItHasFailed() {
Try<String> actualOperationResult = Try.run(() -> { throw new Exception("everything goes bad"); });
assertThat(actualOperationResult.flatMap(flatMapMockCallable), is(actualOperationResult));
verifyZeroInteractions(flatMapMockCallable);
}
@Test
public void flatMapShouldReturnItNewTryWhenItHasSuccess() {
String expectedValue = "3";
Try<String> actualOperationResult = Try.run(() -> expectedValue);
Try<Integer> expectedOperationResult = Try.success(1);
when(flatMapMockCallable.apply(expectedValue)).thenReturn(Try.success(1));
assertThat(actualOperationResult.flatMap(flatMapMockCallable), is(expectedOperationResult));
verify(flatMapMockCallable, times(1)).apply(expectedValue);
}
@Test
public void mapShouldReturnItNewTryWithNewValueWhenItHasSuccess() {
String expectedValue = "1";
Try<String> actualOperationResult = Try.run(() -> expectedValue);
Try<Integer> expectedOperationResult = Try.success(1);
when(mapMockCallable.apply(expectedValue)).thenReturn(1);
assertThat(actualOperationResult.map(mapMockCallable), is(expectedOperationResult));
verify(mapMockCallable, times(1)).apply(expectedValue);
}
@Test
public void mapShouldReturnItselfWhenItFails() {
Try<String> actualOperationResult = Try.run(() -> { throw new Exception("everything goes bad"); });
assertThat(actualOperationResult.map(mapMockCallable), is(actualOperationResult));
verifyZeroInteractions(mapMockCallable);
}
@Test
public void mapShouldReturnItNewTryWithNewValueWhenItHasSuccessWithoutMock() {
String expectedValue = "1";
Try<String> actualOperationResult = Try.run(() -> expectedValue);
Try<Integer> expectedOperationResult = Try.success(1);
assertThat(actualOperationResult.map(Integer::valueOf), is(expectedOperationResult));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment