Как вы проверяете безопасность типов вашего обобщенного API? - PullRequest
5 голосов
/ 07 июля 2011

Вы можете использовать, например, JUnit для тестирования функциональности вашей библиотеки, но как вы проверяете ее безопасность типов в отношении универсальных шаблонов и подстановочных знаков?

Тестирование только по кодам, которые компилируются, является «счастливым путем»; разве вы не должны также проверять свой API на предмет использования, не допускающего безопасность типов, и подтверждать, что эти коды НЕ компилируются?

   // how do you write and verify these kinds of "tests"?

    List<Number> numbers = new ArrayList<Number>();
    List<Object> objects = new ArrayList<Object>();

    objects.addAll(numbers); // expect: this compiles

    numbers.addAll(objects); // expect: this does not compile

Итак, как вы проверяете, что ваш обобщенный API вызывает правильные ошибки во время компиляции? Вы просто создаете набор некомпилируемого кода для тестирования вашей библиотеки и считаете ошибку компиляции успешной проверкой и наоборот? (Конечно, вы должны подтвердить, что ошибки связаны с генериками).

Существуют ли рамки, облегчающие такое тестирование?

Ответы [ 7 ]

6 голосов
/ 13 июля 2011

Поскольку это не тестирование в традиционном смысле (то есть вы не можете «запустить» тест), и я не думаю, что такой инструмент существует, вот что я могу предложить:

  1. Сделать регулярный юнит-тест
  2. Создать в нем код - и правильный код, и неправильный код
  3. Используйте API компилятора Java , чтобы попытаться скомпилировать его.и проверьте результат

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

5 голосов
/ 07 июля 2011

Похоже, вы пытаетесь протестировать Java-компилятор, чтобы убедиться, что он вызовет правильные ошибки компиляции, если вы назначите неправильные типы (в отличие от тестирования вашего собственного API).

Если этоВ таком случае, почему вас также не беспокоит то, что компилятор не даст сбой при назначении целых чисел полям String и при вызове методов для объектов, которые не были инициализированы, и миллион других вещей, которые должны проверять компиляторы при компиляции кода?!

3 голосов
/ 07 июля 2011

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

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

1 голос
/ 18 июля 2011

"Как вы проверяете безопасность типов вашего обобщенного API?"ИМХО, краткий ответ на ваш вопрос должен быть:

  • Не используйте @SuppressWarnings
  • Убедитесь, что вы компилируете без предупреждений (или ошибок)

Более длинный ответ состоит в том, что «безопасность типов» не является свойством API, это свойство языка программирования и его системы типов .Обобщения Java 5 являются типобезопасными в том смысле, что они дают вам гарантию того, что у вас не возникнет ошибка типа (ClassCastException) во время выполнения, если только она не происходит из операции приведения уровня пользователя (и если вы программируете с использованием дженериков, вы редконужно больше таких слепков).Единственный бэкдор - это использование необработанных типов для взаимодействия с кодом до Java 5, но в этих случаях компилятор выдаст предупреждения, такие как печально известное предупреждение «unchecked cast», чтобы указать, что безопасность типов может быть поставлена ​​под угрозу.Однако, за исключением таких предупреждений, Java будет гарантировать безопасность типов.

Так что, если вы не являетесь автором компилятора (или не доверяете компилятору), кажется странным желать проверить «безопасность типов».В приведенном вами примере кода, если вы являетесь разработчиком ArrayList<T>, вам следует только дать addAll наиболее гибкую сигнатуру типа, которая позволит вам написать функционально правильную реализацию .Например, вы можете ввести аргумент как Collection<T>, но также как Collection<? extends T>, где последний предпочтительнее, поскольку он более гибкий.Хотя вы можете чрезмерно ограничивать свои типы, язык программирования и компилятор позаботятся о том, чтобы вы не могли написать что-то, что не является типобезопасным: например, вы просто не можете написать правильную реализацию для addAll, где аргумент имеет тип Collection<?> или Collection<? super T>.

Единственное исключение, о котором я могу подумать, это когда вы пишете фасад для какой-то небезопасной части системы и хотите использовать дженерики для обеспечения каких-то гарантий использованияэтой части через фасад.Например, хотя отражение Java не контролируется как таковое системой типов, Java использует универсальные элементы в таких вещах, как Class<T>, чтобы позволить некоторым отражающим операциям, таким как clazz.newInstance(), интегрироваться ссистема типов.

0 голосов
/ 24 июля 2011

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

Используя Eclipse, я довольно быстро переворачиваю время, когда что-то в Java не компилируется, а сообщения об ошибках довольно просты. Например, если вы ожидаете, что у типа будет определенный вызов метода, а это не так, ваш компилятор скажет вам, что вам нужно знать. То же самое с несоответствиями типов.

Удачи в построении концепций времени компиляции в Java: P

0 голосов
/ 16 июля 2011

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

Я думаю, вы хотите один или несколько из:

  • проверки кода (может поддерживаться инструментом проверки кода, таким как JRT).
  • инструменты статического анализа (FindBugs / CheckStyle)
  • переключение языка на C ++ с реализацией, которая поддерживает концепции (может потребоваться также переключение юниверса на тот, в котором такая реализация существует).

Если вы действительно нуждаетесь в этом как в «тесте», вы можете использовать рефлексию для применения любого желаемого правила, например, «любая функция, начинающаяся с add , должна иметь аргумент, который является универсальным». Это не очень отличается от пользовательского правила Checkstyle , просто неуклюже и менее пригодно для повторного использования.

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

Может быть, вы можете использовать Collections.checkedList () в своем модульном тесте. Следующий пример скомпилируется, но сгенерирует исключение ClassCassException. Пример ниже скопирован с @Simon G.

List<String> stringList = new ArrayList<String>();
List<Number> numberList = Collections.checkedList(new ArrayList<Number>(), Number.class);
stringList.add("a string");
List list = stringList;
numberList.addAll(list);
System.out.println("Number list is " + numberList);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...