Как проверить, если список <? extends Object> является UnmodifableList? - PullRequest
18 голосов
/ 03 декабря 2011

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

У меня есть объект с 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);
}

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

>>> Но я все еще считаю, что это даже близко не элегантное решение!<<< </em>

Ответы [ 8 ]

15 голосов
/ 03 декабря 2011

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

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

7 голосов
/ 03 декабря 2011

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

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

Для вас проблема в том, что упакованный UnmodifiableCollection закрыт для пакета.

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

5 голосов
/ 03 декабря 2011

Вы можете использовать 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>()))

/ e2
Ваша проблема может быть в том, что 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 голосов
/ 04 декабря 2011

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

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

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

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

0 голосов
/ 06 апреля 2018

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

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

* 1006 в guava. Он по определению не может быть изменен, поэтому вам вообще не нужен тест.Это также вызывает меньше путаницы у клиентов класса.
0 голосов
/ 04 апреля 2018

В дополнение к user949300 ответу, в котором говорится, что действительно нужно проверить поведение, и включая комментарий Steve Zobell , который предлагает добавить пустой список, так что в случае список не является неизменяемым, он не изменяется, я добавляю пример того, как может выглядеть проверка на неизменяемость списка.

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

/**
 * Ensures that a list is unmodifiable (i.e. that nothing can be added).
 *
 * Unmodifiable lists can e.g. be created by Collections.UnmodifiableList().
 *
 * @param list a list
 * @param <T> element type of the list
 * @return the list that is verified to be unmodifiable
 */
public static <T> List<T> verifyUnmodifiable(List<T> list) {
    try {
        list.addAll(Collections.emptyList());
    } catch (Exception e) {
        return list;
    }
    throw new IllegalArgumentException("List is modifiable.");
}

Не уверен, что тестирование на удаление, очистку, ... также необходимо. Отсутствие возможности добавления обычно является хорошим признаком неизменяемости.

0 голосов
/ 13 декабря 2016

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

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

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

0 голосов
/ 03 декабря 2011

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

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);
    }
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...