您可以使用例如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在编译时引发了正确的错误?您是否只是构建一个非编译代码来测试您的库,并将编译错误视为测试成功,反之亦然? (当然你必须确认这些错误与泛型相关)。
是否有促进此类测试的框架?
答案 0 :(得分:6)
由于这不是传统意义上的测试(也就是说 - 你不能“运行”测试),我不认为这样的工具存在,这就是我的建议:
您可以为该功能制作一个易于使用的包装,并为符合您要求的任何人提供。
答案 1 :(得分:5)
听起来您正在尝试测试Java编译器,以确保在分配错误类型时(而不是测试您自己的api)会引发正确的编译错误。
如果是这种情况,为什么你不关心编译器在将Integers分配给String字段时没有失败,以及当你在尚未初始化的对象上调用方法时,以及其他数百万个编译器被认为是错误的检查他们何时编译代码?!
答案 2 :(得分:3)
我猜你的问题不仅限于泛型。我们可以向非通用代码提出相同的问题。如果您描述的工具存在,我会感到害怕。有很多人非常乐意测试他们的getter和setter(并尝试在其他人身上强制执行)。现在,他们更乐意编写新的测试,以确保访问其私有字段不会编译!噢,人性!
但是我觉得仿制药更复杂,所以你的问题没有实际意义。对于大多数程序员来说,如果他们能够最终编译他们该死的泛型代码,他们会很高兴。如果一个泛型代码没有编译,这是开发期间的常态,他们并不确定应该责怪谁。
答案 3 :(得分:1)
“您如何测试通用API的类型安全性?”恕我直言,你的问题的简短答案应该是:
@SuppressWarnings
更长的答案是“类型安全”不是API的属性,它是编程语言及其类型系统的属性。 Java 5泛型在某种意义上是类型安全的,它可以保证您在运行时不会出现类型错误(ClassCastException
),除非它源自用户级转换操作(如果您使用泛型编程,你很少需要这样的演员阵容了。唯一的后门是使用原始类型与Java 5之前的代码进行互操作,但对于这些情况,编译器将发出警告,例如臭名昭着的“未经检查的强制转换”警告,以指示类型安全可能会受到损害。但是,如果没有这样的警告,Java将保证您的类型安全。
因此,除非您是编译器编写者(或者您不信任编译器),否则想要测试“类型安全性”似乎很奇怪。在您提供的代码示例中,如果您是ArrayList<T>
的实现者,您应该只关心addAll
最灵活的类型签名,以便您编写功能正确的实现。例如,您可以将参数键入Collection<T>
,也可以键入Collection<? extends T>
,后者是首选,因为它更灵活。虽然您可以过度约束类型,但编程语言和编译器将确保您不能编写非类型安全的东西:例如,您根本无法为参数具有的addAll
编写正确的实现输入Collection<?>
或Collection<? super T>
。
我能想到的唯一例外是,你正在为系统中一些不安全的部分编写一个外观,并希望使用泛型来强制通过外观使用这个部分的某些保证。例如,虽然Java的反射不是由类型系统控制,但是在Class<T>
之类的东西中使用泛型来允许某些反射操作,例如clazz.newInstance()
,与类型系统集成。
答案 4 :(得分:0)
也许您可以在单元测试中使用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);
答案 5 :(得分:0)
测试编译失败听起来像吠叫错误的树,然后用螺丝刀再次剥离树皮。使用正确的工具来完成正确的工作。
我认为你想要一个或多个:
如果你真的需要将它作为'测试',你可以使用反射来强制执行任何所需的规则,比如'任何以 add 开头的函数必须有一个通用的参数'。这与custom Checkstyle rule没有什么不同,只是笨拙且不太可重用。
答案 6 :(得分:0)
好吧,在C ++中,他们试图用概念做到这一点但是从标准启动了。
使用Eclipse当Java中的某些东西无法编译时,我可以快速转换,并且错误消息非常简单。例如,如果您希望某个类型具有某个方法调用而它没有,那么您的编译器会告诉您需要知道的内容。与类型不匹配相同。
祝你好运将编译时概念构建到java:P