Another collector approach:
Collectors:
public final class SingleCollector extends SingleCollectorBase {
@Override
public Function, T> finisher() {
return a -> a.getItem();
}
}
public final class SingleOrNullCollector extends SingleCollectorBase {
@Override
public Function, T> finisher() {
return a -> a.getItemOrNull();
}
}
SingleCollectorBase:
public abstract class SingleCollectorBase implements Collector, T> {
@Override
public Supplier> supplier() {
return () -> new Single<>();
}
@Override
public BiConsumer, T> accumulator() {
return (list, item) -> list.set(item);
}
@Override
public BinaryOperator> combiner() {
return (s1, s2) -> {
s1.set(s2);
return s1;
};
}
@Override
public Set characteristics() {
return EnumSet.of(Characteristics.UNORDERED);
}
}
Single:
public final class Single {
private T item;
private boolean set;
public void set(T item) {
if (set) throw new SingleException("More than one item in collection");
this.item = item;
set = true;
}
public T getItem() {
if (!set) throw new SingleException("No item in collection");
return item;
}
public void set(Single other) {
if (!other.set) return;
set(other.item);
}
public T getItemOrNull() {
return set ? item : null;
}
}
public class SingleException extends RuntimeException {
public SingleException(String message) {
super(message);
}
}
Tests and example usages, albeit lacking parallel tests.
public final class SingleTests {
@Test
public void collect_single() {
ArrayList list = new ArrayList<>();
list.add("ABC");
String collect = list.stream().collect(new SingleCollector<>());
assertEquals("ABC", collect);
}
@Test(expected = SingleException.class)
public void collect_multiple_entries() {
ArrayList list = new ArrayList<>();
list.add("ABC");
list.add("ABCD");
list.stream().collect(new SingleCollector<>());
}
@Test(expected = SingleException.class)
public void collect_no_entries() {
ArrayList list = new ArrayList<>();
list.stream().collect(new SingleCollector<>());
}
@Test
public void collect_single_or_null() {
ArrayList list = new ArrayList<>();
list.add("ABC");
String collect = list.stream().collect(new SingleOrNullCollector<>());
assertEquals("ABC", collect);
}
@Test(expected = SingleException.class)
public void collect_multiple_entries_or_null() {
ArrayList list = new ArrayList<>();
list.add("ABC");
list.add("ABCD");
list.stream().collect(new SingleOrNullCollector<>());
}
@Test
public void collect_no_entries_or_null() {
ArrayList list = new ArrayList<>();
assertNull(list.stream().collect(new SingleOrNullCollector<>()));
}
}