001package com.identityworksllc.iiq.common; 002 003import java.util.NoSuchElementException; 004import java.util.Objects; 005import java.util.StringJoiner; 006 007/** 008 * A functional programming concept that will contain either the left object or 009 * the right object, but never both. This can be used in a variety of functional 010 * data flows. 011 * 012 * @param <L> The left object type 013 * @param <R> The right object type 014 */ 015public final class Either<L, R> { 016 017 /** 018 * Creates an Either object containing the given object on the left. The type of 019 * the right object is undefined. 020 * 021 * @param l The non-null left object 022 * @param <A> The left object type 023 * @return The Either object 024 */ 025 public static <A> Either<A, ?> left(A l) { 026 if (l == null) { 027 throw new IllegalArgumentException("Input to Either.left() must not be null"); 028 } 029 return new Either<>(l, null); 030 } 031 032 /** 033 * Creates an Either object containing the given object on the right. The type of 034 * the left object is undefined. 035 * 036 * @param r The non-null right object 037 * @param <B> The right object type 038 * @return The Either object 039 */ 040 public static <B> Either<?, B> right(B r) { 041 if (r == null) { 042 throw new IllegalArgumentException("Input to Either.right() must not be null"); 043 } 044 return new Either<>(null, r); 045 } 046 047 private final L left; 048 private final R right; 049 050 protected Either(L inputLeft, R inputRight) { 051 left = inputLeft; 052 right = inputRight; 053 } 054 055 /** 056 * @see Object#equals(Object) 057 */ 058 @Override 059 public boolean equals(Object o) { 060 if (this == o) return true; 061 if (!(o instanceof Either)) return false; 062 Either<?, ?> either = (Either<?, ?>) o; 063 return Objects.equals(left, either.left) && 064 Objects.equals(right, either.right); 065 } 066 067 /** 068 * Returns the left object or throws a {@link NoSuchElementException} if this Either contains a right object 069 * @return The left object 070 * @throws NoSuchElementException if this is a right Either 071 */ 072 public L getLeft() { 073 if (left == null) { 074 throw new NoSuchElementException("left"); 075 } 076 return left; 077 } 078 079 /** 080 * Returns the right object or throws a {@link NoSuchElementException} if this Either contains a left object 081 * @return The right object 082 * @throws NoSuchElementException if this is a left Either 083 */ 084 public R getRight() { 085 if (right == null) { 086 throw new NoSuchElementException("right"); 087 } 088 return right; 089 } 090 091 /** 092 * @return True if this Either has a left object 093 */ 094 public boolean hasLeft() { 095 return left != null; 096 } 097 098 /** 099 * @return True if this Either has a right object 100 */ 101 public boolean hasRight() { 102 return right != null; 103 } 104 105 /** 106 * @see Object#hashCode() 107 */ 108 @Override 109 public int hashCode() { 110 return Objects.hash(left, right); 111 } 112 113 /** 114 * @see Object#toString() 115 */ 116 @Override 117 public String toString() { 118 return new StringJoiner(", ", Either.class.getSimpleName() + "[", "]") 119 .add("left=" + left) 120 .add("right=" + right) 121 .toString(); 122 } 123}