Last active
July 20, 2018 18:18
-
-
Save joseronierison/638203d069c0230163096f6e3ea43d4f to your computer and use it in GitHub Desktop.
Try Monad in Java (WIP)
This file contains 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
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() + ")"; | |
} | |
} | |
} | |
This file contains 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
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