Let’s create a class that it stores a list of values and it can compute some value based on stored values. Internally elements are stored in a List. When class is created form the list of values, we can create a collector for it. There is no need for public constructor.
Constructor that accept the List class example
public class Bar<T> { List<T> list = new ArrayList<T>(); public Bar(List<T> list) { this.list.addAll(list); } public void compute(){ // some code } }
Object Bar can be created with constructor call that accepts List object.
Bar<String> collect = new Bar<>( IntStream.range(1, 100) .mapToObj(x -> String.valueOf(x)) .collect(Collectors.toList()) );
Package visible constructor example
public class Foo<T> { List<T> list; Foo() { this.list = new ArrayList<T>(); } public void compute(){ // some code } }
We create collector for Foo class. Collector is in the same package, so that it has access to Foo’s constructor.
public class FooCollector<T> implements Collector<T, List<T>, Foo<T>> { @Override public Supplier<List<T>> supplier() { return () -> new ArrayList<T>(); } @Override public BiConsumer<List<T>, T> accumulator() { return (list, el) -> list.add(el); } @Override public BinaryOperator<List<T>> combiner() { return (listA, listB) -> { listA.addAll(listB); return listA; }; } @Override public Function<List<T>, Foo<T>> finisher() { return (list) -> { Foo<T> foo = new Foo<>(); foo.list.addAll(list); return foo; }; } @Override public Set<Characteristics> characteristics() { return EnumSet.of(Characteristics.UNORDERED); } }
Now we create Foo class from stream of values.
Foo<String> collect = IntStream.range(1, 100) .mapToObj(x -> String.valueOf(x)) .collect(new FooCollector<>());