Book/Effective Java
[Book] ITEM 4) private 생성자로 noninstantiability를 강제할 것
kkkkkkkkkkkk
2022. 3. 1. 20:21
모든 클래스들이 인스턴스화가 필요한 것이 아닙니다.
static 메서드와 static 필드들을 담은 클래스들을 인스턴스화를 시키지 않아도 기능들을 사용 할 수 있는데 굳이 인스턴스화를 시킬 필요는 없다고 봅니다.
역직렬화 기능을 담은 클래스가 있다고 가정해봅시다.
public class ObjectDeSerialization{
private static ByteArrayInputStream byteArrayInputStream;
private static ObjectInputStream objectInputStream;
public static byte[]makeByteDecodeArray(String encodeObject) {
return Base64.getDecoder().decode(encodeObject);
}
public static Object makeDecodeObject(byte[]bytes)throws 예외생략 {
byteArrayInputStream= new ByteArrayInputStream(bytes);
objectInputStream= new ObjectInputStream(byteArrayInputStream);
return objectInputStream.readObject();
}
}
이러한 클래스는 굳이 인스턴스를 만들지 않아도 됩니다. static 메서드와 static 필드들을 담고 있는데 인스턴스를 만들 필요는 없습니다.
그러면 인스턴스화를 막는 방법은 무엇이 있을까요??
👻 private 생성자
첫번째 방법으로는 private 생성자 를 사용하는 방법이 있습니다.
하지만 reflection 기능으로 인스턴스를 생성하는 방법이 있었죠??
@Test
@DisplayName("역직렬화 인스턴스 생성 테스트")
void ObjectDeSerializationTest()throws 예외 생략{
// given & when
// reflection 사용
Class<ObjectDeSerialization> objectDeSerializationClass = ObjectDeSerialization.class;
Constructor<ObjectDeSerialization> declaredConstructor = objectDeSerializationClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
ObjectDeSerialization objectDeSerialization = declaredConstructor.newInstance();
// then
assertThat(objectDeSerialization.getClass().getName()).isEqualTo(ObjectDeSerialization.class.getName());
}
그러면 예외를 던져 막아볼까요??
// 인스턴스 생성시에 AssertionError 예외 발생
private ObjectDeSerialization() {
throw new AssertionError();
}
인스턴스 생성시에 private 생성자에 AssertionError 예외를 던졌으니 예외가 발생 될 것입니다.
@Test
@DisplayName("역직렬화 인스턴스 생성 테스트")
void ObjectDeSerializationTest()throws 예외 생략{
// given & when
// reflection 사용
Class<ObjectDeSerialization> objectDeSerializationClass = ObjectDeSerialization.class;
Constructor<ObjectDeSerialization> declaredConstructor = objectDeSerializationClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
ObjectDeSerialization objectDeSerialization = declaredConstructor.newInstance();
// then
assertThat(objectDeSerialization.getClass().getName()).isEqualTo(ObjectDeSerialization.class.getName());
}
private 생성자를 사용한 경우 주석으로 왜 private 를 사용했는지 설명을 해야합니다.
// 인스턴스화를 막기위한 생성자입니다.
private ObjectDeSerialization() {
throw new AssertionError();
}
👻 abstract Class
두번째 방법으로는 abstract 를 사용하여 인스턴스를 생성하는 것을 막을 수 있습니다.
public abstract class ObjectDeSerialization{
private static ByteArrayInputStream byteArrayInputStream;
private static ObjectInputStream objectInputStream;
public static byte[]makeByteDecodeArray(String encodeObject) {
return Base64.getDecoder().decode(encodeObject);
}
public static Object makeDecodeObject(byte[]bytes)throws IOException, ClassNotFoundException{
byteArrayInputStream= new ByteArrayInputStream(bytes);
objectInputStream= new ObjectInputStream(byteArrayInputStream);
returnobjectInputStream.readObject();
}
}
인스턴스를 생성할려고하면 컴파일 에러가 나옵니다.
@Test
@DisplayName("역직렬화 인스턴스 생성 테스트")
void ObjectDeSerializationTest() {
// given & when & then
ObjectDeSerialization objectDeSerialization = new ObjectDeSerialization(); // 컴파일 에러!!!
}
또한 reflection 기능으로 생성을 하면 InstantiationException 예외가 발생합니다.
@Test
@DisplayName("역직렬화 인스턴스 생성 테스트")
void ObjectDeSerializationTest()throws 예외 생략 {
// given & when
// reflection 사용
Class<ObjectDeSerialization> objectDeSerializationClass = ObjectDeSerialization.class;
Constructor<ObjectDeSerialization> declaredConstructor = objectDeSerializationClass.getDeclaredConstructor();
declaredConstructor.setAccessible(true);
// then
assertThrows(InstantiationException.class,()-> declaredConstructor.newInstance());
}
private 생성자를 사용해서 인스턴스 생성을 막아도 좋지만 abstract 를 사용하여 인스턴스를 막는 것만으로 충분할 것 같다는 생각이듭니다.