Skip to content

Commit e49b620

Browse files
author
Leonhard Bromoloron
committed
add pin methods, seal the interface
1 parent 09e4c08 commit e49b620

4 files changed

Lines changed: 67 additions & 12 deletions

File tree

src/main/java/io/jbock/util/Either.java

Lines changed: 30 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@
77

88
/**
99
* A class that acts as a container for a value of one of two types. An Either
10-
* can be either be a "Left", containing a LHS value or a "Right" containing a RHS value,
11-
* but it cannot be "neither" or "both".
10+
* can be either be a "Left", containing an LHS value or a "Right" containing an RHS value,
11+
* but it cannot be "neither" nor "both".
1212
*
1313
* <p>An Either can be used to express a success or failure case. By convention,
1414
* a Right contains the result of a successful computation,
@@ -17,7 +17,7 @@
1717
* @param <L> the type of the LHS value
1818
* @param <R> the type of the RHS value
1919
*/
20-
public interface Either<L, R> {
20+
public sealed interface Either<L, R> permits Left, Right {
2121

2222
/**
2323
* Returns a Left containing the given non-{@code null} LHS value.
@@ -48,7 +48,7 @@ static <L, R> Either<L, R> right(R value) {
4848
/**
4949
* If this is a Right, returns a Right containing the result of applying
5050
* the mapper function to the RHS value.
51-
* Otherwise returns a Left containing the LHS value.
51+
* Otherwise, returns a Left containing the LHS value.
5252
*
5353
* @param mapper the function to apply to the RHS value, if this is a Right
5454
* @param <R2> the new RHS type
@@ -61,7 +61,7 @@ <R2> Either<L, R2> map(
6161

6262
/**
6363
* If this is a Right, returns the result of applying the mapper function to the RHS value.
64-
* Otherwise returns a Left containing the LHS value.
64+
* Otherwise, returns a Left containing the LHS value.
6565
*
6666
* @param mapper a mapper function
6767
* @param <R2> the new RHS type
@@ -86,7 +86,7 @@ Either<L, R> filter(
8686

8787
/**
8888
* If this is a Left, returns a Left containing the result of applying the mapper function to the LHS value.
89-
* Otherwise returns a Right containing the RHS value.
89+
* Otherwise, returns a Right containing the RHS value.
9090
*
9191
* @param mapper the function to apply to the LHS value
9292
* @param <L2> the new LHS type
@@ -99,7 +99,7 @@ <L2> Either<L2, R> mapLeft(
9999

100100
/**
101101
* If this is a Left, returns the result of applying the mapper function to the LHS value.
102-
* Otherwise returns a Right containing the RHS value.
102+
* Otherwise, returns a Right containing the RHS value.
103103
*
104104
* @param mapper a mapper function
105105
* @param <L2> the new LHS type
@@ -109,6 +109,24 @@ <L2> Either<L2, R> mapLeft(
109109
<L2> Either<L2, R> flatMapLeft(
110110
Function<? super L, ? extends Either<? extends L2, ? extends R>> mapper);
111111

112+
/**
113+
* Returns the receiver object unchanged. This method should only be invoked
114+
* if this is provably a Left.
115+
*
116+
* @return the same object
117+
* @throws UnsupportedOperationException if this is a Right
118+
*/
119+
<R2> Either<L, R2> pinLeft();
120+
121+
/**
122+
* Returns the receiver object unchanged. This method should only be invoked
123+
* if this is provably a Right.
124+
*
125+
* @return the same object
126+
* @throws UnsupportedOperationException if this is a Left
127+
*/
128+
<L2> Either<L2, R> pinRight();
129+
112130
/**
113131
* If this is a Right, returns a Right containing the RHS value.
114132
* If this is a Left, applies the predicate function to the LHS value.
@@ -124,7 +142,7 @@ Either<L, R> filterLeft(
124142

125143
/**
126144
* If this is a Left, returns the result of applying the {@code leftMapper} to the LHS value.
127-
* Otherwise returns the result of applying the {@code rightMapper} to the RHS value.
145+
* Otherwise, returns the result of applying the {@code rightMapper} to the RHS value.
128146
*
129147
* @param leftMapper the function to apply if this is a Left
130148
* @param rightMapper the function to apply if this is a Right
@@ -137,7 +155,7 @@ <U> U fold(
137155

138156
/**
139157
* If this is a Left, performs the {@code leftAction} with the LHS value.
140-
* Otherwise performs the {@code rightAction} with the RHS value.
158+
* Otherwise, performs the {@code rightAction} with the RHS value.
141159
*
142160
* @param leftAction action to run if this is a Left
143161
* @param rightAction action to run if this is a Right
@@ -148,7 +166,7 @@ void ifLeftOrElse(
148166

149167
/**
150168
* If this is a Right, returns the RHS value.
151-
* Otherwise throws an exception produced by the exception supplying function.
169+
* Otherwise, throws an exception produced by the exception supplying function.
152170
*
153171
* @param exceptionSupplier exception supplying function
154172
* @param <X> type of the exception
@@ -176,15 +194,15 @@ default boolean isRight() {
176194

177195
/**
178196
* If this is a Left, returns an {@code Optional} containing the LHS value.
179-
* Otherwise returns an empty {@code Optional}.
197+
* Otherwise, returns an empty {@code Optional}.
180198
*
181199
* @return the LHS value if this is a Left, otherwise an empty {@code Optional}
182200
*/
183201
Optional<L> getLeft();
184202

185203
/**
186204
* If this is a Right, returns an {@code Optional} containing the RHS value.
187-
* Otherwise returns an empty {@code Optional}.
205+
* Otherwise, returns an empty {@code Optional}.
188206
*
189207
* @return the RHS value if this is a Right, otherwise an empty {@code Optional}
190208
*/

src/main/java/io/jbock/util/Left.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,18 @@ public <L2> Either<L2, R> flatMapLeft(Function<? super L, ? extends Either<? ext
5858
return result;
5959
}
6060

61+
@Override
62+
public <R2> Either<L, R2> pinLeft() {
63+
@SuppressWarnings("unchecked")
64+
Left<L, R2> result = (Left<L, R2>) this;
65+
return result;
66+
}
67+
68+
@Override
69+
public <L2> Either<L2, R> pinRight() {
70+
throw new UnsupportedOperationException("not a Right");
71+
}
72+
6173
@Override
6274
public Either<L, R> filterLeft(Function<? super L, Optional<? extends R>> predicate) {
6375
Optional<? extends R> test = predicate.apply(value);

src/main/java/io/jbock/util/Right.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,18 @@ public <L2> Either<L2, R> flatMapLeft(Function<? super L, ? extends Either<? ext
6262
return result;
6363
}
6464

65+
@Override
66+
public <R2> Either<L, R2> pinLeft() {
67+
throw new UnsupportedOperationException("not a Left");
68+
}
69+
70+
@Override
71+
public <L2> Either<L2, R> pinRight() {
72+
@SuppressWarnings("unchecked")
73+
Right<L2, R> result = (Right<L2, R>) this;
74+
return result;
75+
}
76+
6577
@Override
6678
public Either<L, R> filterLeft(Function<? super L, Optional<? extends R>> predicate) {
6779
return this;

src/test/java/io/jbock/util/EitherTest.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import org.junit.jupiter.api.Test;
44

55
import java.io.IOException;
6+
import java.time.LocalDate;
67
import java.util.Objects;
78
import java.util.Optional;
89

@@ -141,4 +142,16 @@ void testOrElseThrow() {
141142
Either<String, String> right = Either.right("2");
142143
assertEquals("2", right.orElseThrow(IllegalArgumentException::new));
143144
}
145+
146+
@Test
147+
void testPin() {
148+
Either<String, Integer> left = Either.left("1");
149+
Either<String, LocalDate> left2 = left.pinLeft();
150+
assertThrows(UnsupportedOperationException.class, left::pinRight);
151+
assertEquals(Either.left("1"), left2);
152+
Either<Integer, String> right = Either.right("2");
153+
Either<LocalDate, String> right2 = right.pinRight();
154+
assertThrows(UnsupportedOperationException.class, right::pinLeft);
155+
assertEquals(Either.right("2"), right2);
156+
}
144157
}

0 commit comments

Comments
 (0)