Как в общем случае указать сериализуемый список - PullRequest
12 голосов
/ 20 января 2009

У меня есть следующий интерфейс:

public interface Result<T extends Serializable> extends Serializable{
    T getResult();
}

С этим интерфейсом я не могу определить переменную типа

Result<List<Integer>>

, поскольку список не сериализуем.

Однако, если я изменю интерфейс на это:

public interface Result<T> extends Serializable{
    T getResult();
}

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

У меня такой вопрос: есть ли способ объявить переменную двух типов, или есть другой способ сделать это, которого я не вижу? Итак, может быть что-то вроде:

(List<Integer> extends Serializable) value = new ArrayList();

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

Спасибо за вашу помощь!

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

Итак, как мне указать, что используемый тип должен быть Serializable, но все же разрешать использование списка (а не конкретной реализации List)?

Ответы [ 7 ]

10 голосов
/ 22 января 2009

Вам необходимо объявить тип переменной как Result<? extends List<Integer>>.

Проверка типа знает, что List не serializable, но подтип List может быть serializable.

Вот пример кода. Реализация интерфейса была только что сделана с анонимными внутренними классами. Вы можете видеть, что getResult вернет List<Integer> на 2-м объекте

   Result<Integer> res = new Result<Integer>() {

        Integer myInteger;

        private static final long serialVersionUID = 1L;

        @Override
        public Integer getResult() {
            return myInteger;
        }

        @Override
        public void addResult(Integer input) {
            this.myInteger = input;
        }
    };

    Integer check = res.getResult();


    Result<? extends List<Integer>> res2 = new Result<ArrayList<Integer>>() {

        ArrayList<Integer> myList;

        private static final long serialVersionUID = 1L;

        @Override
        public ArrayList<Integer> getResult() {
            return myList;
        }

        @Override 
        public void addResult(ArrayList<Integer> input) {
            this.myList = input;
        }

    };

    List<Integer> check2 = res2.getResult();

Редактировать: Сделал пример более полным, реализовав void addResult(T input) интерфейсный метод

3 голосов
/ 22 января 2009

Хотя интерфейс List не реализует Serializable, все встроенные реализации Collection делают. Это обсуждается в учебнике по коллекциям .

В FAQ по дизайну коллекций есть вопрос «Почему коллекция не расширяет Cloneable и Serializable?» , в котором говорится о том, почему Sun разработала ее без расширения Serializable.

2 голосов
/ 20 января 2009

Вы можете просто объявить переменную как Result<ArrayList<Integer>>. Пока вы все еще программируете на интерфейс List, вы не жертвуете заменяемостью.

Я собирался также предложить создать новый интерфейс ListResult:

public interface ListResult<T extends Serializable & List<E extends Serializable>>
        implements Result<T> {
    T getResult();
}

но тогда вам все равно придется объявить переменную как ListResult<ArrayList<Integer>>. Так что я бы пошел по более простому маршруту.

0 голосов
/ 20 января 2009

Я решил эту проблему в качестве интерфейса:

public interface Result<T> extends Serializable{
    T getResult();
}

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

Так, например, вот как должен выглядеть класс ListResult:

public class ListResult<T> implements Result<List<T>>{
    public List<T> getResult(){
        //return result
    }

    public <V extends List<T> & Serializable> void setResult(V result){
        //store result
    }
}
0 голосов
/ 20 января 2009

Вы вроде можете, я думаю:

public class Thing<T extends Serializable> implements Serializable {
    private static class Holder<V extends Serializable> {
        private final V value;
        private Holder(V value) {
            this.value = value;
        }
    }
    private Holder<? extends List<T>> holder;
    private <V extends List<T> & Serializable> void set(V value) {
        holder = new Holder<V>(value);
    }
}

Это выглядит уродливо для тебя?

Могу ли я предложить вам не пытаться принудительно реализовать Serializable с использованием системы статических типов Java? Это всего лишь интерфейс, потому что аннотации не были тогда. Это просто не практично, чтобы обеспечить его соблюдение. OTOH, вы можете применить его, используя средство проверки типов, которое выполняет статический анализ в закрытой системе.

0 голосов
/ 20 января 2009

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

public interface Result<T extends List<? extends Serializable>> {}

Таким образом, вы можете определить что-то вроде:

Result<List<Integer>> r;

Но что-то подобное не скомпилируется:

Result<List<List>> r;

Теперь, если вы хотите использовать результат как, скажем, для Integer, так и для List, тогда тип не обязательно должен быть сериализуемым, верно? В таком случае я не совсем понимаю, какова ваша цель.

0 голосов
/ 20 января 2009

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

Возможно, вам не следует расширять Serializable, если вы не добавляете функциональность, связанную с сериализацией объектов. Вместо этого ваш класс, который реализует Result, должен также реализовать Serializable.

...