Мгновенная реализация интерфейса интерфейса во время выполнения

Ниже мой код, написанный для следующей задачи (на основе вопроса SO) :

  

Для Java Collection (из N ) создайте ArrayList, содержащий N   коллекции одного типа с одним элементом в каждом.

Вот мой код для такого метода:

public static <E> ArrayList<Collection<E>> SplitCollection (Collection<E> col) throws InstantiationException, IllegalAccessException {
    ArrayList<Collection<E>> listOfCollections = new ArrayList<>();
    for(E el: col) {
        // creating an empty collection of type E
        Collection<E> colEl = col.getClass().newInstance();
        colEl.add(el);
        listOfCollections.add(colEl);
    }
    return listOfCollections;
}
  1. Есть ли лучший способ создать экземпляр времени выполнения класса реализации для интерфейса?
  2. Я переусердствовал что-либо в свете стирания типа?
  3. Другая критика кода?

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

  

Все классы общего назначения Collection (которые обычно   внедрить Collection косвенно через один из своих подинтерфейсов)   должен предусматривать два «стандартных» конструктора: пустота (без аргументов)   конструктор, который создает пустой collection и конструктор с   один аргумент типа Collection, который создает новую коллекцию   с теми же элементами, что и аргумент. По сути, последний   конструктор позволяет пользователю копировать любую коллекцию, создавая   эквивалентный набор желаемого типа реализации. Здесь нет   способ обеспечения соблюдения этого соглашения (поскольку интерфейсы не могут содержать   конструкторы), но все универсальные Collection   реализации в библиотеках платформы Java соответствуют.

Итак, я писал свой код для универсальных Collection реализаций .

Я никогда не думал, что этот код будет использоваться для производственных целей. Я просто хотел посмотреть, как далеко программирование для интерфейса вместе с Generics может получить вас, прежде чем иметь , чтобы использовать конкретный тип и реализацию.

12 голосов | спросил PM 77-1 24 J000000Thursday14 2014, 06:06:09

2 ответа


14

Две части этого обзора:

  1. 1-лайнер для newInstance ()
  2. общий механизм метода

newInstance ()

Этот код будет работать, но только для подмножества коллекций. Например, существует множество реализаций Collection, которые не имеют доступного конструктора по умолчанию. Что делать, если входная коллекция:

Collection<String> input = Arrays.asList(new String[]{"hello","world"});

Вызов newInstance в классе этого списка не удастся .... потому что это класс, называемый java.util.Arrays$ArrayList, и это не общественность вообще ...

«Спецификация» для этой проблемы, вероятно, надуманна, и нет никаких ожиданий, что она должна вести себя хорошо, когда входы плохие. Как надуманный пример, я думаю, что надуманный код в порядке.

Что приводит к следующему, что нужно делать?

Ну, я не думаю, что есть правильная вещь. Если вход требует поддержки bare 'Collection', тогда у вас нет надежды.

В реальном примере я бы, вероятно, указал метод, позволяющий конкретным известным коллекциям ожидать такую ​​программу. Тогда не используйте newInstance вообще.

В качестве альтернативы я бы использовал стиль Java-8 Supplier , и у вас есть вход, который снабжает вас необходимой коллекцией и переносит нагрузку на пользователя вашего метода. Что-то вроде:

public interface CollectionSupplier<T> {
    Collection<T> supply();
}

, а затем ваш метод будет объявлен как:

public static <E> ArrayList<Collection<E>> SplitCollection (
        final Collection<E> col, final CollectionSupplier<E> supplier) {
    .....
    Collection<E> colEl = supplier.supply();
    ....
}

Это также устранит уродливые исключения, которые вы объявляете бросать ... как бы то ни было, я бы (плохая практика) «попробовал» весь цикл и «улавливал» уродливые исключения и заменял их оберткой IllegalStateException, который объясняет, что внутренняя коллекция не может быть создана.

Общие

В целом ваш метод аккуратный и хорошо структурированный. Я не могу ошибаться, учитывая ограничения проблемы.

Обновить . На самом деле, метод должен иметь нижний регистр 's' для запуска имени. Как я пропустил это в первый раз? Должен быть splitCollection not SplitCollection

Я бы предпочел, чтобы метод объявил ввод col как final: public static <E> ArrayList<Collection<E>> SplitCollection (final Collection<E> col), потому что это привычка, в которой я нахожусь ...

Обычно я бы рекомендовал не возвращать данные, введенные как ArrayList, потому что реализация не должна отражаться, она должна быть просто List, но в этом случае спецификация требует возвращаемого значения ArrayList.

Bonus

Выполнение Java 8 этого было бы хорошим упражнением.

Вот что я хотел бы попробовать:

public static <T, U extends Collection<T>, V extends Collection<T>> ArrayList<V> splitCollection (U col, Supplier<V> supplier) {

    return col.stream().map(v -> {
           V subcol = supplier.get();
           subcol.add(v);
           return subcol;
        }).collect(Collectors.toCollection(ArrayList::new));
}

и он будет использоваться как:

public static void main(String[] args) {
    System.out.println("Result:"
       + splitCollection(Arrays.asList("hello","world"), ArrayList::new));
}

, который дает результат:

Result:[[hello], [world]]

Обратите внимание, что в примере с Java 8 я разрешаю подкатегории любого поддерживаемого типа, к которому вы можете добавить контент. Таким образом, для приведенного выше примера вход является недоступным списком (без конструктора по умолчанию), но поставщик дает вам то, что вам нужно.

ответил rolfl 24 J000000Thursday14 2014, 06:32:44
6

Существует проблема философии с этой проблемой. Весь смысл интерфейса Collection заключается в том, чтобы выступать в качестве интерфейса высокого уровня для коллекций. Он отбрасывает конкретные детали реализации по дизайну. Попытка вернуть его обычно будет вводить только точные проблемы, которые существует для интерфейса Collection.

Поэтому вы не можете надежно завершить этот вопрос. Метод newInstance не всегда будет работать, особенно если вы начнете использовать его в домашних сборниках. Например, метод newInstance () требует, чтобы объект имел пустой конструктор, вы можете легко написать коллекцию, для которой это не удается. Если вы попробуете это в Googles Immutable Collections, то либо эта строка, либо когда вы попытаетесь добавить объект, потерпит неудачу (я не знаю, имеет ли он конструктор, вы должны использовать фабрику или строитель).

На практике этот код обычно работает, но включение его в производственное приложение вызывает тонкую ошибку, когда клиент пытается использовать ее в коллекции, которая не может быть создана таким образом.

ответил phil_20686 24 J000000Thursday14 2014, 14:21:08

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132