如何使 Map 的专业化不可修改?
How to make a specialization of Map unmodifiable?
我目前正在通过一个中型编码示例来更新我的 Java 知识。我有一个数据结构 Map<String, String>
,通常用 new LinkedHashMap<>()
初始化它以保留插入顺序。我在我的代码中经常使用它,我想摆脱声明重复。在 C++ 中,我会为地图添加别名,但据我所知,在 Java 中没有别名。
所以我想出了像这样子 class 泛型的想法:
public class Attributes extends LinkedHashMap<String, String> {
public Attributes() {
super();
}
public Attributes(Map<? extends String, ? extends String> map) {
super(map);
}
}
到目前为止这看起来不错,但现在我想创建一个不可修改的副本,因为属性应该是 immutable/unmodifiable 数据结构的一部分。在我使用这个之前:
Map<String, String> unmodifiableAttributes = Collections.unmodifiableMap(
new LinkedHashMap<>(attributes)
);
这不适用于派生 class,我试过这个:
Attributes unmodifiableAttributes = Collections.unmodifiableMap(
new Attributes(attributes)
);
编译器用 Incompatible types
拒绝它。
是否有一种简单的方法来获取此类子class的不可修改(或不可变)副本?还是我的想法完全错误?我不想写一个全功能的装饰器,就几行代码。
更新
到目前为止,我想做的事情似乎还没有很好的解决方案。我查看了 Java 集合 class 的源代码,并且有内部 classes 用于不可修改的地图和类似的集合。这些用于包装输入集合并由相应的静态方法返回。可以重新实现这一点,但我认为开销太大。
我们对 LSP violation 的讨论比原来的问题更多,这确实也是一个有趣的问题。
Collections.unmodifiableMap
returns a Map<K,V>
所以你必须像这样使用它:
Map<String, String> unmodifiableAttributes = Collections.unmodifiableMap(
new Attributes(attributes)
);
并且您无法将返回的对象转换为 Attributes
,例如:
Attributes unmodifiableAttributes = (Attributes) Collections.unmodifiableMap(
new Attributes(attributes)
);
因为 Collections.unmodifiableMap
returns private static UnmodifiableMap
的实例所以你会得到一个 ClassCastException
。 Attributes
不是 UnmodifiableMap
.
的子类型
此外,我认为在您的情况下,直接使用 LinkedHashMap
而不是从中创建派生的 class 会更容易,因为正如我所见,功能与原始功能没有区别.然后使用从 Collections.unmodifiableMap
返回的对象作为 Map
.
你不能让 subclass LinkedHashMap
不可修改,因为它会违反 Liskov 可替换性:LinkedHashMap
被记录为可变的,所以所有 subclasses也一定是。
你还有一个额外的问题,就是要使地图不可修改实际上需要做很多工作:你不仅有像 put
和 remove
这样明显的方法,你还有像 clear
、putAll
、putIfAbsent
、computeIfAbsent
、computeIfPresent
。然后你必须担心视图-returning 方法:entrySet
、keySet
、values
都必须 return 不可修改的视图。我确定我错过了几个也需要重写的方法,但我的观点仍然是使可变映射不可修改并非易事。
但是,您可以拥有不可修改的 Map 实现。最简单的方法是扩展 AbstractMap
,并委托给实际的 LinkedHashMap
:
public class Attributes extends AbstractMap<String, String> {
private final LinkedHashMap<String, String> delegate;
public Attributes() {
this(Collections.emptyMap());
}
public Attributes(Map<? extends String, ? extends String> map) {
this.delegate = new LinkedHashMap<>(map);
}
// Override the methods you need to, and that are required by AbstractMap.
// Details of methods to override in AbstractMap are given in Javadoc.
}
但我也会质疑您的属性 class 是否真的需要实现像 Map
接口一样通用的东西 - 如果您需要这种通用性,您可以简单地使用 Map
直接。
我目前正在通过一个中型编码示例来更新我的 Java 知识。我有一个数据结构 Map<String, String>
,通常用 new LinkedHashMap<>()
初始化它以保留插入顺序。我在我的代码中经常使用它,我想摆脱声明重复。在 C++ 中,我会为地图添加别名,但据我所知,在 Java 中没有别名。
所以我想出了像这样子 class 泛型的想法:
public class Attributes extends LinkedHashMap<String, String> {
public Attributes() {
super();
}
public Attributes(Map<? extends String, ? extends String> map) {
super(map);
}
}
到目前为止这看起来不错,但现在我想创建一个不可修改的副本,因为属性应该是 immutable/unmodifiable 数据结构的一部分。在我使用这个之前:
Map<String, String> unmodifiableAttributes = Collections.unmodifiableMap(
new LinkedHashMap<>(attributes)
);
这不适用于派生 class,我试过这个:
Attributes unmodifiableAttributes = Collections.unmodifiableMap(
new Attributes(attributes)
);
编译器用 Incompatible types
拒绝它。
是否有一种简单的方法来获取此类子class的不可修改(或不可变)副本?还是我的想法完全错误?我不想写一个全功能的装饰器,就几行代码。
更新
到目前为止,我想做的事情似乎还没有很好的解决方案。我查看了 Java 集合 class 的源代码,并且有内部 classes 用于不可修改的地图和类似的集合。这些用于包装输入集合并由相应的静态方法返回。可以重新实现这一点,但我认为开销太大。
我们对 LSP violation 的讨论比原来的问题更多,这确实也是一个有趣的问题。
Collections.unmodifiableMap
returns a Map<K,V>
所以你必须像这样使用它:
Map<String, String> unmodifiableAttributes = Collections.unmodifiableMap(
new Attributes(attributes)
);
并且您无法将返回的对象转换为 Attributes
,例如:
Attributes unmodifiableAttributes = (Attributes) Collections.unmodifiableMap(
new Attributes(attributes)
);
因为 Collections.unmodifiableMap
returns private static UnmodifiableMap
的实例所以你会得到一个 ClassCastException
。 Attributes
不是 UnmodifiableMap
.
此外,我认为在您的情况下,直接使用 LinkedHashMap
而不是从中创建派生的 class 会更容易,因为正如我所见,功能与原始功能没有区别.然后使用从 Collections.unmodifiableMap
返回的对象作为 Map
.
你不能让 subclass LinkedHashMap
不可修改,因为它会违反 Liskov 可替换性:LinkedHashMap
被记录为可变的,所以所有 subclasses也一定是。
你还有一个额外的问题,就是要使地图不可修改实际上需要做很多工作:你不仅有像 put
和 remove
这样明显的方法,你还有像 clear
、putAll
、putIfAbsent
、computeIfAbsent
、computeIfPresent
。然后你必须担心视图-returning 方法:entrySet
、keySet
、values
都必须 return 不可修改的视图。我确定我错过了几个也需要重写的方法,但我的观点仍然是使可变映射不可修改并非易事。
但是,您可以拥有不可修改的 Map 实现。最简单的方法是扩展 AbstractMap
,并委托给实际的 LinkedHashMap
:
public class Attributes extends AbstractMap<String, String> {
private final LinkedHashMap<String, String> delegate;
public Attributes() {
this(Collections.emptyMap());
}
public Attributes(Map<? extends String, ? extends String> map) {
this.delegate = new LinkedHashMap<>(map);
}
// Override the methods you need to, and that are required by AbstractMap.
// Details of methods to override in AbstractMap are given in Javadoc.
}
但我也会质疑您的属性 class 是否真的需要实现像 Map
接口一样通用的东西 - 如果您需要这种通用性,您可以简单地使用 Map
直接。