Ответ 1
Нет; вы не можете этого сделать.
Метод по умолчанию не совпадает с методами расширения; они могут быть определены только в исходном интерфейсе.
Я часто хочу сопоставить один список в другом списке. Например, если у меня был список людей, и мне нужен список их имен, я бы хотел:
ЦЕЛЬ
List<Person> people = ... ;
List<String> names = people.map(x -> x.getName());
Что-то вроде этого возможно с Java 8:
JAVA 8 VERSION
List<String> names = people.stream()
.map(x -> x.getName())
.collect(Collectors.toList());
Но это явно не так хорошо. На самом деле, я думаю, что использование Guava является более чистым:
ВЕРСИЯ GUAVA
List<String> names = Lists.transform(people, x -> x.getName());
Однако мне нравится цепочки. Итак, возможна ли моя цель?
Я слышал, что люди говорят, что методы Java 8 по умолчанию похожи на методы расширения С#. С помощью метода расширения С# я мог бы легко добавить вспомогательный метод к IEnumerable<T>
:
public static IEnumerable<TRet> Map<T, TRet>(this IEnumerable<T> list, Func<T, TRet> selector)
{
return list.Select(selector);
}
Однако я не могу понять, как использовать методы по умолчанию для расширения существующего интерфейса.
Кроме того, это, очевидно, тривиальный пример. В общем, я хотел бы расширить возможности интерфейсов List<T>
и Iterable<T>
, чтобы упростить взаимодействие с потоками api.
Нет; вы не можете этого сделать.
Метод по умолчанию не совпадает с методами расширения; они могут быть определены только в исходном интерфейсе.
Если вы хотите иметь легкий вид для List
, применяя Function
и поддерживая цепочку, вы можете сделать это следующим образом:
import java.util.*;
import java.util.function.Function;
import java.util.stream.Stream;
public class MappingList<E> extends AbstractList<E> {
// using this helper class we avoid carrying <S> with the public API
static final class Source<E,S> {
final List<S> list;
final Function<? super S, ? extends E> mapper;
Source(List<S> l, Function<? super S, ? extends E> m) {
list=l;
mapper=m;
}
E get(int index) { return mapper.apply(list.get(index)); }
<T> Source map(Function<? super E, ? extends T> f) {
Objects.requireNonNull(f);
return new Source<>(list, mapper.andThen(f));
}
Stream<E> stream() { return list.stream().map(mapper); }
Stream<E> parallelStream() { return list.parallelStream().map(mapper); }
}
final Source<E,?> source;
private MappingList(Source<E,?> s) {
Objects.requireNonNull(s);
source=s;
}
@Override
public E get(int index) {
return source.get(index);
}
@Override
public int size() {
return source.list.size();
}
@Override
public Stream<E> stream() {
return source.stream();
}
@Override
public Stream<E> parallelStream() {
return source.parallelStream();
}
public <T> MappingList<T> map(Function<? super E, ? extends T> f) {
return new MappingList<>(source.map(f));
}
public static <S,T> MappingList<T> map(
List<S> l, Function<? super S, ? extends T> f) {
Objects.requireNonNull(l);
if(l instanceof MappingList)
return ((MappingList<S>)l).map(f);
return new MappingList<>(new Source<>(l, f));
}
}
Он поддерживает создание GUAVA-стиля сопоставленного списка, все еще позволяя использовать API Stream
с отображенным списком, который оценивает все значения лениво:
public static void main(String[] arg) {
List<String> strings=Arrays.asList("a", "simple", "list");
List<Integer> ints=MappingList.map(strings, s->compute(s));
List<Integer> results=MappingList.map(ints, i->compute(i));
for(int result:results) {
System.out.println("first result: "+result);
System.out.println("Not computing any more values");
break;
}
System.out.println();
System.out.println(" interacting with stream API:");
System.out.println(results.stream().filter(i-> i>500).findFirst());
}
public static int compute(String s) {
System.out.println("doing computation for "+s);
return Integer.parseInt(s, 36);
}
public static int compute(int i) {
System.out.println("doing computation for "+i);
return i*i;
}
doing computation for a doing computation for 10 first result: 100 Not computing any more values interacting with stream API: doing computation for a doing computation for 10 doing computation for simple doing computation for 1724345618 Optional[410277188]
Если вы хотите создать List
с предварительно вычисленными значениями из него, вы можете просто использовать new ArrayList<>(mappedList)
.
Поедая свою собачью пищу и реализуя то, что я предложил в комментарии (UNTESTED, но она должна работать), обратите внимание, что вам следует использовать super
, если это необходимо, это не моя сильная сторона):
public final class ListTransformer<T>
{
private final List<T> inputList;
public static <X> ListTransformer<X> transform(final List<X> inputList)
{
return new ListTransformer<X>(inputList);
}
private ListTransformer(final List<T> inputList)
{
this.inputList = inputList;
}
public <U> List<U> using(final Function<T, U> f)
{
return inputList.stream().map(f).collect(Collectors.toList());
}
}
Использование:
import static wherever.is.ListTransformer.transform;
//
final List<String> names = transform(personList).using(x -> x.getName());
Расширяемые методы С# являются отличными и обеспечивают и упрощают механизм расширения коллекций С# собственными методами.
Но теперь в java есть потоки. Основываясь на ответе @fge, я пришел с этим фрагментом, чтобы писать собственные методы расширения в потоках:
public final class StreamExtender<T>
{
private final Stream<T> _inputStream;
public static <T> StreamExtender<T> extend(final Stream<T> inputStream)
{
return new StreamExtender<>(inputStream);
}
private StreamExtender(final Stream<T> inputStream)
{
this._inputStream = inputStream;
}
public <U> List<U> extensionMethod(final Function<T, U> f)
{
// your own code.
return _inputStream.map(f).collect(Collectors.toList());
}
}
И чтобы увидеть это в действии:
Integer[] array = { 1, 2, 3};
List<String> result = StreamExtender.extend(stream(array)).extensionMethod(Object::toString);