我最近偶然发现了JDK API的一个非常有趣的警告,即Class.getConstructors()
方法。 它的方法签名是这样的:
Constructor<?>[] getConstructors()
有趣的是, Class.getConstructor(Class...)
返回一个Constructor<T>
,并保持了<T>
:
Constructor<T> getConstructor(Class<?>... parameterTypes)
为什么会有区别,即为什么第一个方法不返回Constructor<T>[]
?
让我们考虑一下Javadoc:
请注意,尽管此方法返回一个Constructor <T>对象的数组(也就是此类的构造函数的数组),但此方法的返回类型为Constructor <?> [],而不是Constructor <T> []。预期。 这种信息量较少的返回类型是必需的,因为从此方法返回后,可以修改该数组以容纳不同类的Constructor对象,这将违反Constructor <T> []的类型保证。
这是一个艰难的过程。 从历史上看,这是怎么发生的:
Java 1.0 / Oak:数组
在Java 1.0( Oak编程语言的直接继承者)中,已经引入了数组。 实际上,它们是在Java 1.2中引入的collections API之前引入的。 数组遭受着我们今天所知道的所有问题,包括它们的协变,这在运行时导致了很多问题,这些问题在编译时无法检查:
Object[] objects = new String[1];
objects[0] = Integer.valueOf(1); // Ouch
Java 1.1:反射API
缺少“不错的” collection API, Class.getConstructors()
方法唯一可能的返回类型是Constructor[]
。 当时的合理决定。 当然,您可能会犯上述相同的错误:
Object[] objects = String.class.getConstructors();
objects[0] = Integer.valueOf(1); // Ouch
但是除了上述内容之外,您还可以正确地编写以下代码:
Constructor[] constructors = String.class.getConstructors();
constructors[0] = Object.class.getConstructor();// Muahahahahahahaha
Java 1.2:Collections API
Java从很早的时候就一直向后兼容,甚至从Oak开始。 到目前为止,在这个Stack Overflow问题中,关于Oak的一些向后兼容性已泄漏到Java中,这是一个非常有趣的历史研究。
虽然使用集合来设计反射API是很自然的,但是现在已经为时已晚。 更好的解决方案可能是:
List getConstructors()
但是,请注意,我们还没有泛型,因此数组实际传达的类型信息比集合多。
Java 1.5:泛型
在Java 5中,
Constructor[] getConstructors()
至
Constructor<?>[] getConstructors()
已经出于上述原因进行了处理。 现在,使用集合的替代API肯定会更好:
List<Constructor<T>> getConstructors()
但是船开了。
Java,丑陋的疣
Java充满了这些小警告。 它们都记录在Javadocs中,并且经常在Stack Overflow中记录。 就在昨天,我们在Map
和ConcurrentHashMap
记录了与全新API相关的新警告 。
“管理权:发人深省”,关于所有这些警告以及Brian Goetz维护这些警告的难易程度的很好演讲可以在这里看到:
演讲摘要:
语言设计师谈论他们正在设计的语言时
翻译自: https://www.javacodegeeks.com/2015/03/the-java-legacy-is-constantly-growing.html