Создание пустого Списка <String>на основе Обобщения - PullRequest
0 голосов
/ 03 мая 2018

Итак, я сейчас пытаюсь реализовать метод, который выполняет некоторую фильтрацию по спискам независимо от их фактического типа. Вот фактический метод:

public static <T extends List<String>> T filterList(T list, Predicate <String> predicate) {

    T newList = ???
    list.forEach(s -> {
        if (predicate.test(s)) newList.add(s);
    });
    return newList;

}

Таким образом, универсальный тип T в основном представляет собой некоторую реализацию List, такую ​​как ArrayList или LinkedList, и независимо от их фактической реализации, я хочу выполнить некоторую фильтрацию по предикату, переданному в качестве параметра. Тип возврата метода совпадает со списком, который передается в качестве параметра. Но как можно создать пустой список на основе T (см. Строку 2)? Чтобы показать вам, как метод предназначен для использования, я привел пример. В следующем примере фильтруется ArrayList на основе длины содержащихся строк:

ArrayList<String> listOfNames = new ArrayList<>();
listOfNames.add("stackoverflowuser");
listOfNames.add("sitaguptana");
listOfNames.add("nyan cat");
listOfNames.add("pedro");

Predicate<String> lengthUnderTen = (string) -> string.length() < 10;

ArrayList <String> result = filterList(listOfNames,lengthUnderTen);

Ответы [ 5 ]

0 голосов
/ 03 мая 2018

У вас есть базовый класс, который уже позволяет вам аккуратно программировать интерфейс. Вы можете спроектировать этот метод API так, чтобы он возвращал объект List, сохраняя общий тип на уровне элемента.

Обратите внимание, что этот шаблон уже предоставляется стандартным API (Список / Коллекция + Поток), поэтому вам не нужно создавать его заново. - см. примечание внизу.

Если у вас нет никаких ограничений относительно типа списка , возвращаемого методом, то выбор этой реализации зависит от того, какой тип списка он возвращает (используя список массивов ниже):

public static <T> List<T> filterList(List<T> list, Predicate<T> predicate) {

    List<T> newList = new ArrayList<>(); //You can choose a different type here
    list.forEach(s -> {
        if (predicate.test(s)) newList.add(s);
    });

    return newList;
}

Если вы предоставляете вызывающей стороне возможность выбрать , какой тип списка создается , то, возможно, вам следует воспользоваться фабрикой:

public static <U, T extends List<U>> T filterList(T list, 
      Predicate<U> predicate, Supplier<T> newListFactory) {

    T newList = newListFactory.get(); //You can choose a different type here
    list.forEach(s -> {
        if (predicate.test(s))
            newList.add(s);
    });

    return newList;
}

Примечание : этот шаблон уже предоставляется API коллекций:

java.util.stream.Stream.filter(Predicate<? super T>)

Это позволяет вам делать то же самое, за исключением того, что создание возвращаемого списка (скажем, вы запустили collect(Collectors.toList()))

0 голосов
/ 03 мая 2018

Вы можете создать класс с помощью отражения, передав в качестве параметра List<String>.
Фактически вам не нужно указывать какой-либо подстановочный знак для вашего списка.

public static List<String> filterList(List<String> list,  Predicate<String> predicate) throws InstantiationException, IllegalAccessException {

    List<String> newList = list.getClass()
                               .newInstance();
    list.forEach(s -> {
        if (predicate.test(s)) newList.add(s);
    });
    return  newList;
}

Но обратите внимание, что более чистый способ - использовать поток для сбора нового списка, если реализация не имеет значения:

public static List<String> filterList(List<String> list,  Predicate<String> predicate) throws InstantiationException, IllegalAccessException {

  return list.stream()
             .filter(predicate)
             .collect(Collectors.toList());
}
0 голосов
/ 03 мая 2018

Пусть вызывающий абонент также наберет Supplier<T>.

public static <T extends List<String>> T filterList(T list, Predicate <String> predicate, Supplier<T> listCreator) {

    T newList = listCreator.get();
    list.forEach(s -> {
       // ...
0 голосов
/ 03 мая 2018

Если я правильно понимаю ваш вопрос, то я не понимаю, зачем вам вообще использовать дженерики здесь.

Следующая функция примет любой класс, который расширяет List, как параметр, например ArrayList, LinkedList и т. д.:

public static List<String> filterList(List<String> list, Predicate<String> predicate) {
    return list.stream().filter(predicate).collect(Collectors.toList());
}

Полный пример:

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.Collectors;

public class Example {

    public static void main(String[] args) {
        ArrayList<String> example1 = new ArrayList<>();
        example1.add("abc");
        example1.add("ghe");

        LinkedList<String> example2 = new LinkedList<>();
        example2.add("foo");
        example2.add("bar");

        List<String> result1 = filterList(example1, s -> s.contains("a"));
        List<String> result2 = filterList(example2, s -> s.contains("f"));
    }

    public static List<String> filterList(List<String> list, Predicate<String> predicate) {
        return list.stream().filter(predicate).collect(Collectors.toList());
    }
}
0 голосов
/ 03 мая 2018

Если бы вы могли изменить свой метод как

public static <T> List<T> filterList(List<T> list, Predicate<T> predicate) {

    return list.stream().filter(predicate).collect(Collectors.toList());
}

Выглядит чисто, потому что работает для любого типа списка, а не только для списка . Этот метод будет более общим.

...