我正在编写一些处理HTTP响应标头(一个不可变的集合一旦填充)的代码,并发现自己有如下代码:
private Map<String,String> headers;
public ctor() {
this.headers = new Hashtable<String,String>();
// ... some more ... (actually, this fills this.headers based on ctor parameters)
}
public Map<String,String> getResponseHeaders() {
Map<String,String> temp = new Hashtable<String,String>();
temp.putAll(this.headers);
return temp;
}
看着这个,我突然想到,虽然我知道headers
现在的类型,并且不可否认,所讨论的类足够短(目前总共约50行,并且不准备增长更多),在这个特定的情况下,这不是一个主要问题,如果问题空间有点复杂,那么在getResponseHeaders()
中,我可能不知道this.headers
(使用这个特定示例中的名称)被实例化到的类型。此外,在一个大的类中,我想返回集合的副本,跟踪许多不同的实例成员变量和它们的类型(可能由许多因素决定;列表大小是一个可能的(/em>考虑因素)可能会非常复杂。
两个相关的问题:
没有成熟的依赖注入框架,并且禁止全面使用反射(因为性能损失),对于一般情况,是否有任何方法使temp
的实例类型与上述代码中的this.headers
的实例类型相同?
在什么情况下,这可能会产生很大的不同?
可以调用this.headers.clone()
,但是需要强制转换结果。此外,这只适用于类型是可克隆的情况,而克隆正是您想要的(例如,在本例中是浅克隆)。
除此之外,没有可能,因为访问this.headers
的运行时类型(您称之为实例类型)已经涉及反射。然而,我不会因为有人说反射很慢就不使用反射。并不是所有的反射操作都很慢(例如,我希望在一个对象上调用getClass()
会非常快),并不是所有的操作都会在你的应用程序中产生性能差异。这取决于更多的因素,即,这个操作执行的频率,优化器有多好等等。
反射的问题更多的是没有通用的方法来实例化特定的类型。如果类需要构造函数参数,则需要知道将哪些对象传递给构造函数。
所以,想想这个:你真的需要确保它是完全相同的运行时类型吗?通常情况并非如此。针对接口进行编程的要点在于,您通常不应该关心实际的运行时类型,而应该只关心它实现了正确的接口(此处为Map
)。因此,即使this.headers
在未来的运行时类型实际上是TreeMap
,当前形式的getter也可以。
然而,在这种特殊情况下,我会建议一个完全不同的解决方案。我认为在每次调用getResponseHeaders
时创建整个集合的副本是不好的(例如,我猜这比反射方法调用要昂贵得多)。相反,您可以将集合的不可修改的视图传递给调用者。使用此不可修改视图,调用方将无法更改集合,而这通常是您想要确保的。因此,通常不需要复制整个集合。
return java.util.Collections.unmodifiableMap(this.headers);
注意,通过这种方式,如果在调用getResponseHeaders
之后更改this.headers
,则调用者将看到映射的更改。