When using generic types in a language like Java, nesting generics is a code smell. That is to say, a type like List<Map<String, T>>
is probably a sign that you've gone off the path and should rethink how you're structuring your program. Similarly, types that depend on more than one or two generic type parameters are probably a code smell as well.
If those are a "code smell" this code Adam S found is a "code sewage treatment plan in dire need of a visit from the Environmental Protection Agency".
public interface VolumeStream<V extends Volume, I> extends BaseVolumeStream<VolumeStream<V, I>, V, I>{
default <M extends MutableVolume, M2 extends M, U extends UnmodifiableVolume> M2 merge(V second, VolumeMerger<I, ? super U> merger, M2 destination, VolumeFiller<? extends M, ? extends I> applier) {
return merge(VolumeReducer.of(() -> second, () -> (U) getVolume().asUnmodifiableVolume(), () -> destination, merger, applier));
}
<M extends MutableVolume, M2 extends M, U extends UnmodifiableVolume> M2 merge(VolumeReducer<V, I, M, M2, U> reducer);
}
public interface VolumeReducer<V extends Volume, T, M extends MutableVolume, M2 extends M, U extends UnmodifiableVolume> {
Supplier<V> getSecond();
Supplier<U> getReference();
Supplier<M2> getAccumilator();
VolumeMerger<T, U> GetReducer();
VolumeFiller<M2, T> getFinisher();
public static <NV extends Volume,
NT,
NM extends MutableVolume,
NM2 extends NM,
NU extends UnmodifiableVolume>
VolumeReducer<NV, NT, NM, NM2, NU> of(final Supplier<NV> second,
final Supplier<NU> reference,
final Supplier<NM2> accumilator,
final VolumeMerger<NT, ? super NU> reducer,
final VolumeFiller<? extends NM, ? extends NT> finisher) {
return new Impl<>(second, reference, accumilator, reducer, finisher);
}
}
Not only is that a nest of types which is impossible to understand, it doesn't even manage to get full type safety- VolumeStream
presumably inherits the method getVolume
through BaseVolumeStream
, but getVolume
doesn't have a signature of type U
(anything which extends UnmodifiableVolume
, presumably)- so there's actually a cast required: (U) getVolume().asUnmodifiableVolume()
.
But hey, even if the code is ugly, that hopefully gives us a clean, readable calling convention, right? We're gonna call this code more often that we're gonna write it. How do they call this?
stream.<MutableBlockVolume<? extends MutableBlockVolume<?>>, GenerationRegion, UnmodifiableBlockVolume<?>>merge(this, merger, region, VolumeFiller.BLOCK_APPLIER);
Oh.
Adam adds:
This code tries to express a transformation pipeline for streaming game data, and instead expresses how much I'd like to be writing in literally anything else.