tg-me.com/javatg/1869
Last Update:
💥 Задача: Почему эта структура «ломается» в многопоточной среде?
import java.util.*;
public class BrokenImmutable {
private final Map<String, List<String>> data;
public BrokenImmutable(Map<String, List<String>> input) {
this.data = Collections.unmodifiableMap(input);
}
public Map<String, List<String>> getData() {
return data;
}
public static void main(String[] args) {
Map<String, List<String>> base = new HashMap<>();
base.put("key", new ArrayList<>(List.of("a")));
BrokenImmutable bi = new BrokenImmutable(base);
Map<String, List<String>> d = bi.getData();
d.get("key").add("🚨");
System.out.println(bi.getData());
}
}
🔍 Разбор:
С первого взгляда кажется, что BrokenImmutable — иммутабельный класс. Мы оборачиваем Map через Collections.unmodifiableMap, и поле data — final.
Но проблема в глубине структуры.
unmodifiableMap запрещает перезапись ключей, но не делает элементы внутри truly immutable. В данном случае, значение по ключу "key" — это ArrayList, которую легко модифицировать.
💣 В main() мы получили доступ к внутреннему списку и… тихо сломали инвариант класса, добавив "🚨".
✅ Решение:
Копировать и оборачивать значения внутри Map.
Сделать глубокую защиту:
public BrokenImmutable(Map<String, List<String>> input) {
Map<String, List<String>> copy = new HashMap<>();
for (Map.Entry<String, List<String>> e : input.entrySet()) {
copy.put(e.getKey(), List.copyOf(e.getValue())); // immutable list
}
}
Теперь никто не сможет мутировать data, даже если получит на него ссылку.
🧠 Вопрос на подумать:
А что если вместо ArrayList внутри Map был бы CopyOnWriteArrayList или ImmutableList от Guava? Почему CopyOnWriteArrayList — тоже плохой выбор для truly immutable структур?
@javatg
BY Java
Warning: Undefined variable $i in /var/www/tg-me/post.php on line 283
Share with your friend now:
tg-me.com/javatg/1869