웹 애플리케이션 기동 시 properties에 있는 설정 정보들을 컬렉션 객체에 저장하고 이를 가져다가 쓰는 경우가 많은데 이 객체에 변경을 막는 제약 조건을 걸고 싶을 때가 있다.
즉, read-only 한 객체를 만들고 싶은 경우인데 이럴 때 다음의 메소드를 사용하면 좋을 것 같다.
Collections.unmodifiableMap
Collections.unmodifiableList
해당 메소드는 파라미터로 전달 받은 컬렉션 객체에 어떠한 변경이라도 발생하면 예외를 return한다.
테스트 코드는 다음과 같다.
package collection;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import org.junit.Test;
public class CollectionsTest {
@Test
public void unmodifyMapTest() {
Map<Integer,
String> testMap = new HashMap<Integer, String>();
testMap.put(1,
“test1”);
testMap.put(2,
“test2”);
Map<Integer,
String> unmodifyTestMap = Collections.unmodifiableMap(testMap);
assertThat(“test1”, is(unmodifyTestMap.get(1)));
assertThat(“test2”, is(unmodifyTestMap.get(2)));
boolean exceptionThrown = false;
try {
unmodifyTestMap.put(3,
“test3”); //
unmodifyTestMap 객체 변경 시 예외
}
catch (UnsupportedOperationException ex) {
ex.printStackTrace();
exceptionThrown
= true;
}
assertTrue(exceptionThrown);
}
}
문득 어떻게 해서 객체의 변경을 감지한 후 예외를 던지는지 궁금해 졌다.
unmodifiableMap 메소드는 다음과 같은 구조를 가지고 있고, UnmodifiableMap 클래스의 타입을 return한다.
public static <K,V> Map<K,V> unmodifiableMap(Map<? extends K, ? extends V> m) {
return new
UnmodifiableMap<K,V>(m);
}
자~ 그럼 UnmodifiableMap 클래스를 들여다 보자.
Map 인터페이스를 구현한 UnmodifiableMap 클래스에서 put, remove, putAll 호출 시 UnsupportedOperationException 예외를 return하도록 설계되어 있다. (생각보다 간단하게 구현되어 있음. 역시 Java API)
private static class UnmodifiableMap<K, V> implements Map<K, V>, Serializable {
// use serialVersionUID from JDK 1.2.2 for
interoperability
private static final long serialVersionUID = -1034234728574286014L;
private final Map<? extends K, ?
extends V> m;
UnmodifiableMap(Map<?
extends K, ? extends V> m) {
if (m == null)
throw new NullPointerException();
this.m = m;
}
public int size() {
return m.size();
}
public boolean isEmpty() {
return m.isEmpty();
}
public boolean containsKey(Object key) {
return m.containsKey(key);
}
public boolean containsValue(Object val) {
return m.containsValue(val);
}
public V get(Object key) {
return m.get(key);
}
public V put(K key, V value) {
throw new UnsupportedOperationException();
}
public V remove(Object key) {
throw new UnsupportedOperationException();
}
public void putAll(Map<? extends K, ?
extends V> m) {
throw new UnsupportedOperationException();
}
public void clear() {
throw new UnsupportedOperationException();
}
}
인터페이스를 이용한 느슨한 설계가 어떤 장점을 주는지 확인할 수 있는 좋은 예인 것 같다.