제리 devlog

[이펙티브자바] 인스턴스화를 막으려거든 private 생성자를 사용해라 + java static 개념 본문

Java

[이펙티브자바] 인스턴스화를 막으려거든 private 생성자를 사용해라 + java static 개념

제리 . 2020. 10. 29. 23:33

이번 item은 책 분량상 약 1쪽이 조금 넘는 분량이기 때문에 다루는 내용이 굉장히 간략하다.  따라서 자주 등장했던 static의 개념도 추가로 다뤄 보고자한다.

 

책의 도입부는 이렇게 시작된다.

 "단순히 static 메서드와 static 필드만을 담은 클래스를 만들고 싶은 때가 있을 것이다. 객체 지향적으로 사고하지 않는 이들이 종종 남용하는 방식이기에 그리 곱게 보이지는 않지만, 분명히 나름 쓰임새가 있다. 예컨데 java.lang.Math, java.util.Arrays처럼 기본 타입 값이나 배열 관련 메서드들을 모아놓을 수 있다."

여기서 객체 지향적인 사고란 어떤 것을 의미하는지 생각해보기전 static의 특성에 대해 생각해보자.

new 키워드를 통해 생성된 객체는 heap영역에 생성되고 GC에 의해 관리된다. 그러나 static 키워드로 선언된 경우 heap영역이 아닌 static영역에서 관리된다. static영역은 GC가 관리하지 않기 때문에 프로그램 실행시 메모리가 static영역에 할당되고 프로그램 종료시까지 사라지지않는다. 무분별한 남용은 시스템 퍼포먼스에 악영향을 줄 수 있다.

 

1. static field

출처 - https://gmlwjd9405.github.io/2018/08/04/java-static.html

위의 개념을 가지고 static 필드만을 담은 클래스의 경우 객체 지향적인 사고를 하지 않는 부분에 대해 생각해보자.

static 맴버는 생성되는 모든 인스턴스에 대해서 값을 공유한다. 아래 예시를 보자.

fruit클래스는 과일의 이름과 가격을 전달받아 인스턴스를 생성한다. 여기서 name은 static, price는 non-static으로 선언되어있다.  fruit1, fruit2의 인스턴스를 생성해 출력해보면 아래와 같은 결과가 나온다.

 

name이 동일한 banana로 출력되었다. static 맴버는 새롭게 만든 인스턴스일지라도 값을 공유한다. 이로인해 객체 지향의 특성인 다형성에 문제가 생긴다. 위의 예시에서는 극단적으로 잘못된 예를 들었지만 유용하게 작용하는 부분도 분명 존재한다. 객체간에 공통적으로 갖는 정보를 표현할 때 사용한다면 분명 유용한 방법이 될 것이다. 하지만 static 필드만을 담은 클래스는 특별한 목적을 갖지 않는다면 객체 지향적이지 못한 방법일 것이다.

 

2. static method

 

출처 - https://gmlwjd9405.github.io/2018/08/04/java-static.html

non-static 필드는 객체 생성시 만들어진다.  객체의 생성없이 사용가능한 static 메서드에서는 사용이 불가능하다.

단, static 메서드안에 새롭게 필드를 정의하는건 가능한다. 

 

출처 - https://gmlwjd9405.github.io/2018/08/04/java-static.html

3. 오버라이딩이 불가능하다.

오버라이딩이 불가능하다는 것은 단순히 객체의 다형성을 훼손한다고 판단할 수 있다. 이런 측면에서 저자가 객체 지향적인 사고를 하지 않는 이들이 정적 메서드와 정적필드만을 사용한 클래스를 만든다고 한 것으로 보인다.

 

그렇다면 저자가 예시로든 java.lang.Math 클래스를 생각해보자. Math클래스는 연산에 관한 내용을 담고있다. 연산 방법은 미리 정해져있고 새롭게 오버라이딩 될 필요가 없다. 이런 속성을 가지고 있기 때문에 사용자가 새롭게 Math 인스턴스를 만들어 조작할 필요가 없다.(오히려 위험할 수 있음) 때문에 Math클래스에서는 메서드를 static의 형태로 제공한다. 

 

그럼에도 Math클래스를 인스턴스로 만들려고하는 경우가 있을 수 있다(클라이언트의 실수인경우)

위와 같이 오류가 발생하고 인스턴스 생성을 방지하고있다. 저자는 이번장에서 인스턴스화를 막는 방법에대해 소개하고 있다.

 

api를 확인해보면 종종 인스턴스 생성을 방지하지 않는 경우가 있다. 대체적으로 class안에 생성자를 설정하지않아 컴파일러가 기본적으로 생성해주는 기본 생성자가 만들어지는 경우다.  그렇다면 클래스 자체를 추상클래스로 만드는 방법은 어떨까?

 

위와같이 추상클래스를 만들면 인스턴스 생성은 불가하지만 static 메서드는 사용할 수 있다. 하지만 이것은 하위 클래스를 구현하면 인스턴스를 생성할 수 있다.

이런식으로 추상 메서드를 구현하게되면 인스턴스가 생성된다. 여기서 문제는 abstract클래스로 만들게 됐을때 사용자는 상속해서 사용하라는 뜻으로 받아들일 수도 있다는 것이다. 

 

따라서, 저자는 private 생성자를 만들고 private 생성자를 만든 의도가 직관적이지 않으니 주석을 추가하라고한다.

실제 Math클래스가 구현된 것을 확인해보면 아래와 같이되어있다. 

생성자안에 throw new AssertionError();코드를 추가해준다면 클래스 안에서라도 실수로 생성자를 호출하는 것을 방지해준다.  또한, 이 방법은 상속을 불가능하게 한다. 모든 생성자는 명시적/묵시적으로 상위 클래스의 생성자를 호출하는데 하위 클래스에서 private 생성자를 호출할 수 없기 때문이다.

Comments