Как проверить, есть ли список <? extends Object> является UnmodifableList?

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

У меня есть объект с List<NoMatter>, чтобы предлагать такие методы, как addNoMatter(NoMatter nm) вместо того, чтобы позволить клиенту API просто делать .getNoMatters().add(nm);. Я всегда возвращаю неизменяемую версию этого списка, поэтому клиент все еще может иметь список. Я делаю это следующим образом:

public List<NoMatter> getNoMatters() {
    return Collections.unmodifiableList(this.noMatters);
}

Проблема в том, что когда я выполняю свои тесты, я просто не могу проверить, имеет ли этот объект тип UnmodifiableList. Моя первая попытка:

@Test
public void checkIfListIsImmutable(){
    assertTrue("List is not immutable", this.myObj.getNoMatters() instanceof UnmodifiableList);
}

Случается, что я не могу импортировать тип UnmodifiableList ни java.util.Collections$UnmodifiableRandomAccessList, что я получаю, когда я пытаюсь System.out.println(myObj.getNoMatters().getClass().getName()); на консоли.

Итак, как я могу достичь этого?

PS: Я знаю, что могу пройти этот тест, выполнив:

@Test(expected = UnsupportedOperationException.class)
public void checkIfListIsImmutable(){
     this.myObj.getNoMatters().add(null);
}

РЕДАКТИРОВАТЬ: Вышеупомянутый тест не дает мне понять, что список Im, который имеет дело, не является неизменным, поскольку мне нужно будет иметь тесты для каждого метода, который может изменить мой первоначальный список, включая .remove(),.clear(),.shuffle() и т.д.! вот почему я не думаю, что это хороший способ продолжить.

→ > Но я все еще верю, что это даже близко к элегантному решению! & Л; < <

Ответ 1

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

this.myObj.getNoMatters().getClass().getSimpleName().equals("UnmodifiableCollection")

Проблема для вас - это обернутое UnmodifiableCollection, которое является частным пакетом.

Я не вижу ничего плохого в ожидании исключения там, но это только я.

Ответ 2

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

Аналогично, если бы я хотел проверить, что я не могу передать null некоему методу, я бы прошел null в тесте и ожидал исключение IllegalArgumentException.

Ответ 3

Вы можете использовать Class#isInstance для проверки.

Collections.unmodifiableList(someList).getClass().isInstance(listToCheck);

/e1
Возвращает false.

Collections.unmodifiableList(new ArrayList<Object>()).getClass().isInstance(new ArrayList<Object>())

Возвращается true.

Collections.unmodifiableList(new ArrayList<Object>()).getClass().isInstance(Collections.unmodifiableList(new ArrayList<Object>()))

/е2
Ваша проблема может быть связана с тем, что Collections#unmodifiableList возвращает некоторое время UnmodifiableList и UnmodifiableRandomAccessList в остальное время (см. Ниже код). Однако, поскольку UnmodifiableRandomAccessList расширяет UnmodifiableList, если вы получаете экземпляр UnmodifiableList для проверки, вы будете золотыми.

Чтобы получить экземпляр UnmodifiableList, вы можете использовать Collections.unmodifiableList(new LinkedList<Object>()). LinkedList не реализует RandomAccess, поэтому экземпляр UnmodifiableRandomAccessList не возвращается.

Код Collections#unmodifiableList:

public static <T> List<T> unmodifiableList(List<? extends T> list) {
        return (list instanceof RandomAccess ?
                new UnmodifiableRandomAccessList<>(list) :
                new UnmodifiableList<>(list));
}

Заголовок класса UnmodifiableRandomAccessList:

static class UnmodifiableRandomAccessList<E> extends UnmodifiableList<E> implements RandomAccess

Ответ 4

Почему вы хотите проверить, что ваш список является неизменным? Ваше "альтернативное" решение - путь: вы должны сосредоточиться на тестировании поведения вашего класса, а не на его состоянии.

Что я имею в виду, вам все равно, возвращает ли ваш метод экземпляр UnmodifiableList или что-то еще. Какая разница? Ваш тест, конечно же, не будет. Если позже вы измените реализацию, чтобы достичь такого же поведения, ваш тест не должен терпеть неудачу.

Итак, что вы хотите проверить? Что пользователи ничего не могут добавить в список? Затем напишите тест (как вы сказали), который добавляет что-то в список и ожидает исключения. Что они не могут удалить что-либо из списка? Сделайте то же самое с другим тестом. И так далее.

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

Ответ 5

вид взлома, но попробуйте:

import java.util.*;
public class Main {
    public static void main(String[] args) {
        List<Integer> list=new LinkedList<Integer>();
        list.add(1);
        List<Integer> unmodifiableList=Collections.unmodifiableList(list);
        System.out.println(unmodifiableList.getClass());
        if(unmodifiableList.getClass().getName().contains("UnmodifiableList"))
            System.out.println(true);
    }
}

Ответ 6

Я хочу, чтобы что-то вроде Collections.examine(T element) выполняло задание без фактической модификации коллекции.

Помимо самого проголосовавшего ответа, имейте в виду, что существует Collections.EmptyList (по крайней мере), который также является другим немодифицируемым списком.

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