问题
There are 2 overloaded methods.
Each of these methods converts a list of one type to a list of a different type. But the first method uses a comparator.
class SomeClass {
public static <T, G> List<G> toListOfNewType(List<T> inputList,
Function<T, G> mapperFunction,
Comparator<? super G> comparator) {
return Stream.ofNullable(inputList)
.flatMap(List::stream)
.map(mapperFunction)
.sorted(comparator)
.collect(Collectors.toList());
}
public static <T, G> List<G> toListOfNewType(List<T> inputList,
Function<T, G> mapperFunction) {
return Stream.ofNullable(inputList)
.flatMap(List::stream)
.map(mapperFunction)
.collect(Collectors.toList());
}
}
As you can see, most of the lines are duplicated. How can I get rid of the second method so that passing null to the first as a comparator will not break it?
In other words, how to make the first work without a comparator?
回答1:
Use an if
statement to check for null
:
public static <T, G> List<G> toListOfNewType(List<T> inputList,
Function<T, G> mapperFunction,
Comparator<? super G> comparator) {
Stream<G> stream = Stream.ofNullable(inputList)
.flatMap(List::stream)
.map(mapperFunction);
if (comparator != null)
stream = stream.sorted(comparator);
return stream.collect(Collectors.toList());
}
public static <T, G> List<G> toListOfNewType(List<T> inputList,
Function<T, G> mapperFunction) {
return toListOfNewType(inputList, mapperFunction, null);
}
回答2:
While I second the goal of removing code duplication on the implementation side, I consider assigning a special meaning to null
instead of clear overloaded methods a step into the wrong direction.
You can still have two methods for the caller's convenience with no code duplication in the implementation, e.g.:
public static <T, G> List<G> toListOfNewType(List<T> inputList,
Function<T, G> mapperFunction,
Comparator<? super G> comparator) {
List<G> resultList = toListOfNewType(inputList, mapperFunction);
if(!resultList.isEmpty()) resultList.sort(comparator);
return resultList;
}
public static <T, G> List<G> toListOfNewType(List<T> inputList,
Function<T, G> mapperFunction) {
return inputList==null? Collections.emptyList():
inputList.stream().map(mapperFunction).collect(Collectors.toCollection(ArrayList::new));
}
I would even consider dropping the support for a null
input list, as it only leads to hiding problems instead of solving them.
An alternative would be:
public static <T, G> List<G> toListOfNewType(List<T> inputList,
Function<T, G> mapperFunction,
Comparator<? super G> comparator) {
return toListOfNewTypeImpl(inputList, mapperFunction, s -> s.sorted(comparator));
}
public static <T, G> List<G> toListOfNewType(List<T> inputList,
Function<T, G> mapperFunction) {
return toListOfNewTypeImpl(inputList, mapperFunction, UnaryOperator.identity());
}
private static <T, G> List<G> toListOfNewTypeImpl(List<T> inputList,
Function<T, G> mapperFunction,
UnaryOperator<Stream<G>> lastOp) {
return lastOp.apply(inputList.stream().map(mapperFunction)).collect(Collectors.toList());
}
回答3:
I might just have tested it with a very small input (and of course less efficient for performing element comparisons even when not required), but possibly this could do it :
public static <T, G> List<G> toListOfNewType(List<T> inputList, Function<T, G> mapperFunction) {
return toListOfNewType(inputList, mapperFunction, (a, b) -> 0);
}
回答4:
This would be a interesting thing to ask in my team over some code review too... The problem here is that if you allow a nullable
Comparator
you are defeating the purpose a bit. An argument
that is a Comparator
and is null
is perfectly valid meaning of a natural order sort, meaning this is perfectly valid:
List<Integer> list = new ArrayList<>();
list.add(2);
list.add(1);
Collections.sort(list, null);
Even if your internal implementation would not allow it as it is, meaning this:
List.of(4, 3, 2)
.stream()
.sorted(null)
.forEachOrdered(System.out::println);
would throw a NullPointerException
.
So unless you clearly document what your intention is supposed to be for this method, users might be confused (I would).
IMHO it's not a lot of code to "duplicate" and you could enforce that users pass a Function
that returns "something" that is Comparable
by doing:
public static <T, G extends Comparable<? extends G>> List<G> sortedToListOfNewType(
List<T> inputList,
Function<T, G> mapperFunction) {
return Stream.ofNullable(inputList)
.flatMap(List::stream)
.map(mapperFunction)
.sorted()
.collect(Collectors.toList());
}
public static <T, G> List<G> toListOfNewType(
List<T> inputList,
Function<T, G> mapperFunction) {
return Stream.ofNullable(inputList)
.flatMap(List::stream)
.map(mapperFunction)
.collect(Collectors.toList());
}
The downside is that 1) duplicate code - that you try to avoid at all means it seems 2) these can not be overloaded methods - since the erasure is the same and the compiler would not allow this.
But, as said, this approach is very subjective.
回答5:
I don't think there is an easy way to do that or I don't know. But if you would like to try my library: abacus-util, here is what you can do:
StreamEx.of(inputList)
.map(mapperFunction)
.__(s -> comparator == null ? s : s.sort(comparator))
.toList()
来源:https://stackoverflow.com/questions/58782150/removing-overloaded-method-in-java