Java, объявить переменную с несколькими интерфейсами? - PullRequest
18 голосов
/ 14 июля 2009

Можно ли в Java объявить поле / переменную, тип которой является несколькими интерфейсами? Например, мне нужно объявить Map, что также Serializable. Я хочу убедиться, что переменная ссылается на сериализуемую карту. Интерфейс Map не расширяет Serializable, но большинство реализаций Map Serializable.

Я почти уверен, что ответ - нет.

Follow : я полностью осведомлен о создании нового интерфейса, расширяющего как Map, так и Serializable Это не будет работать, поскольку существующие реализации (такие как HashMap) не реализуют мой новый интерфейс.

Ответы [ 9 ]

12 голосов
/ 14 июля 2009

Вы можете сделать это с помощью дженериков, но это не красиво:

class MyClass<T,K,V extends Serializable & Map<K,V>> {

   T myVar;

}
7 голосов
/ 14 июля 2009

Это можно сделать с помощью некоторых общих приемов:

    public <T extends Map<?,?> & Serializable> void setMap(T map)

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

7 голосов
/ 14 июля 2009

Нет необходимости объявлять поле / переменную таким образом. Тем более что его можно тестировать только во время выполнения, а не во время компиляции. Создайте установщик и сообщите об ошибке, если переданная карта не реализует Serializable.

Ответы, рекомендующие создать собственный интерфейс, конечно, не очень практичны, так как они будут активно запрещать отправку в виде карт и сериализуемых, но не вашего специального интерфейса.

5 голосов
/ 14 июля 2009
public interface MyMap extends Map, Serializable {
}

определит новый интерфейс, объединяющий Map и Serializable.

Очевидно, что вам необходимо предоставить подходящую реализацию этого (например, MyMapImpl), и затем вы можете предоставить переменные ссылки типа MyMap (или Map, или Serializable, в зависимости от требований).

Чтобы ответить на ваши разъяснения, вы не можете модифицировать поведение (например, сериализуемую карту). У вас есть , чтобы иметь интерфейс и некоторую соответствующую реализацию.

3 голосов
/ 14 июля 2009

Вы можете достичь этого, создав собственный интерфейс, который расширяет нужные вам интерфейсы

public interface SerializableMap<K, V> extends Map<K, V>, Serializable {

}
2 голосов
/ 14 июля 2009

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

Если вы посмотрите в SDK, то обнаружите, что они редко (если вообще когда-либо) передают реальные объекты коллекции.

Причина в том, что это не очень хорошая идея. Коллекции крайне незащищены.

Большую часть времени вы хотите сделать копию, прежде чем выдать ее и передать копию, чтобы любые изменения в коллекции не изменили среду для чего-то другого, на что она опиралась. Кроме того, многопоточность превращается в кошмар - даже с синхронизированной коллекцией!

Я видел два решения, одно из которых - всегда извлекать массив и передавать его. Вот как это делает SDK.

Другой вариант - ВСЕГДА переносить коллекции в родительский класс (я имею в виду инкапсулировать, а не расширять). Я приобрел эту привычку, и это того стоит. На самом деле это ничего не стоит, потому что вы все равно не дублируете все методы сбора (фактически вы редко дублируете ни один из них). Фактически, в итоге вы перемещаете функциональность «Утилиты» из других классов, распределенных по всему вашему коду, в класс-оболочку, где он должен был быть в первую очередь.

Любой метод с сигнатурой, которая соответствует «method (collection, ...)», почти наверняка должен быть методом-членом этой коллекции, как и любые циклы, которые перебирают коллекцию.

Мне просто нужно выбрасывать это время от времени, потому что это одна из тех вещей, которые я некоторое время не получал (потому что никто не отстаивал эту концепцию). Всегда кажется, что у него будет какой-то недостаток, но, сделав это некоторое время и увидев проблемы, которые он решил, и код, который он устранял, я даже не могу себе представить никаких возможных недостатков, это просто хорошо.

0 голосов
/ 28 августа 2018

В моем случае это сработало только для объявления конкретного типа:

    HashMap<String, String> mySerializableMap = new HashMap<>();

Это позволило мне использовать методы Map (например, put) и передать карту методам, которые требовали Serializable, без приведения. Не идеально, когда мы научились программировать на интерфейсы, но достаточно хорошо для меня в ситуации, в которой я находился.

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

public interface SerializableMap<K, V> extends Map<K, V>, Serializable {
    // No new methods or anything
}

Следующим шагом также является объявление нового класса:

public class SerilizableHashMap<K, V> extends HashMap<K, V> implements SerializableMap<K, V> {
    private static final long serialVersionUID = 4302237185522279700L;
}

Этот класс объявлен для реализации комбинированного интерфейса и, следовательно, может использоваться везде, где требуется один из этих типов. Он расширяет класс, который уже реализует каждый из интерфейсов в отдельности, поэтому нам больше ничего не нужно делать. И теперь у нас есть то, что вы просили. Пример использования:

public static void main(String[] args) {
    SerializableMap<String, String> myMap = new SerilizableHashMap<>();

    // myMap works as a Map
    myMap.put("colour1", "red");
    myMap.put("colour2", "green");

    // myMap works as a Serializable too
    consumeSerializable(myMap);
}

public static void consumeSerializable(Serializable s) {
    // So something with the Serializable
}

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

Ссылка: Что означает «программировать на интерфейс»?

0 голосов
/ 14 июля 2009

Нет, вам в значительной степени нужно будет разыграть.

0 голосов
/ 14 июля 2009

Вы не можете сделать это, если хотите продолжать использовать существующие реализации Map.

Альтернативой может быть создание вспомогательного класса и добавление метода, подобного этому:

public static Serializable serializableFromMap(Map<?, ?> map) {
    if (map instanceof Serializable) {
        return (Serializable)map;
    }
    throw new IllegalArgumentException("map wasn't serializable");
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...