일반적인 프로그래밍 원칙(effective java 3판 스터디, 자바에서 경계해야할 것들 간단 정리)
일반적인 프로그래밍에서 하지 말아야할 것
먼저 일반적인 프로그래밍에서 하지 말아야할 것에 대해 말하기 전에 소개하고 싶은 것이 있다.
이 글은 이펙티브 자바 3판을 읽으면서 처음쓰는 글이다.
그런데 포스트의 내용은 첫 장에 나오는 내용이 아니라 중간에 나오는 내용이다.
왜 이렇게 글을 쓰냐면 이 책은 스터디 방법이 좀 달라야한다고 생각하기 때문이다.
이 책을 한 번 읽고 완전히 이해하고 적용하는 사람은 없다고 생각한다.
그냥 쭉 읽고 이해한 부분을 정리하고 적용할 수 있는 건 해보면 그걸로 충분한 것 같다.
이해하지 못한 부분은 또 읽으면 되니까. 좀 더 레벨업하고 와서 보면 또 다르게 이해가 되는 것도 있겠지 하는 마음의 스터디 방법이다. (추천합니다.)
각설하고 간단하게 정리하며 한 번 더 공부해본다.
1. 지역 변수 범위를 최소화 하기
- 지역 변수 범위를 최소화하면 유지보수성이 높아지고 오류를 낼 가능성은 낮아진다.
지역 변수 범위를 최소화하기 위한 기법 3 가지
1) 가장 처음 쓰일 때 선언하기
이제는 C언어에서도 코드 상단이 아닌 사용해야할 위치에서 변수를 선언한다. 가독성도 좋아진다.
2) 선언과 동시에 초기화하기
초기화에 필요한 정보다 부족하면 선언은 미뤄야 한다.
try-catch는 try안에서 선언하는 것이 예외를 멀리 퍼지지 않게해서 좋다. 부득이하게 try-catch문 외부에서도 지역 변수를 사용해야한다면 try문 앞에서 선언해야한다.
3) 메서드를 최대한 작게 유지하고 그 메서드는 1개의 기능에만 집중하기
만약 메서드에서 여러 기능을 수행하는데 지역 변수가 살아있다면 괜히 다른 기능에서 접근이 가능해져서 문제를 일으킬 수도 있다.
2. for문 보다는 for-each 문을 사용하기
- 배열이냐 컬렉이냐에 따라 문법 형태가 바뀌지 않고 변수가 조잡하지 않아 실수할 일이 적다.
for(int i=0;i<a.length;i++), Iterator, ... 뭐 이런게 안나와서 가독성도 좋고 성능도 떨어지지 않는다.
* for-each문을 사용할 수 없는 경우 (이럴 때만 기존 for문을 사용하자.)
1) 컬렉션을 순회하면서 선택된 원소를 제거하는 경우
이럴 때는 반복자의 remove() 메서드를 사용해야한다. 물론 컬렉션은 removeIf()를 사용하면 for-each도 사용할 수 있다.
2) 배열을 순회하면서 원소의 값을 변경하는 경우
일부나 전체 값을 변경하는 경우에는 배열의 인덱스를 사용해야한다.
3) 병렬 순회를 사용해야하는 경우
컬렉션을 스트림으로 병렬 순회하는 경우 인덱스를 사용하여 엄격하게 처리해야하므로 일반 for문을 사용해야한다.
참고로 for-each문을 사용하려면 Iterable<E> 인터페이스를 구현해야한다.
3. 라이브러리 활용하기
- 아주 기본적인 이야기지만 자기보다 훨씬 고수인 개발자가 심혈을 기울여서 만들어 놓은 표준라이브러리를 적극 사용하자.
구글의 구아바(guava), java.lang, java.util 의 하위패키지는 꼭 한 번씩 사용해보도록 하자. 아주 기본기가 충실한 개발자가 될 수 있을 것이다.
4. 정확한 값이 필요할 때는 Float, Double을 쓰지말자
- 금융과 관련된 계산처럼 정밀한 값을 표현할 때는 Float, Double을 쓰면 안된다.
Float, Double은 0.1, 0.01등을 표현할 수 없기 때문이다.
대신 BigDecimal, int, long을 사용하도록 하자.
5. 박싱된 기본 타입(Integer, Double, Boolean)보다 기본 타입(int, double, boolean)을 사용하자.
- 엄연히 박싱된 기본 타입은 클래스고 내부적으로 식별성 속성을 갖는다.
기본 타입은 언제나 유효하지만 박싱된 기본 타입은 객체기 때문에 null을 가질 수 있다.
박싱된 타입은 '==' 연산을 할 때 문제를 일으킬 수 있다. 고유 식별성 속성이 같은지를 검사해버리기 때문이다.
naturalOrder.compare(new Integer(42), new Integer(42)); 의 값은 1을 출력한다. (같으면 0)
연산 같은 곳에 잘못하면 오토 언박싱/박싱이 일어나면서 오버헤드가 엄청나게 발생할 수도 있다.
6. 다른 타입이 더 적절하다면 문자열 타입을 피하자
- 문자열이 여러 메서드도 제공해주고 사용하기 편리하다고 느껴 자주 사용되지만 더 적합한 타입이 있으면 boolean, enum, float등을 쓰는게 더 좋다.
7. 문자열 연결은 느리니 주의해서 사용하라
- 문자열은 불변클래스라 두 문자열을 연결할 때 복사본을 만들어서 연결하고 새 문자열을 만든다.
성능이 떨어질 수 밖에 없다. 다른 포스트에서 언급했듯이 stringbuilder, stringbuffer를 사용하도록 한다.
8. 객체는 인터페이스를 사용해 참조하라
- 예를들면 Map<String, Object> map = new HashMap<>(); 이런식으로 앞에 선언에서 인터페이스를 사용해 참조하라는 얘기다.
이렇게 하면 HashMap서 다른 더 효율적인 Map을 사용하려고 할 때 변경에서 자유롭다.
9. 리플렉션보다는 인터페이스를 사용하라
- 리플렉션은 컴파일타임에 타입검사를 할 수 없기때문에 없는 메서드를 호출하려한다든지 하면 에러가 발생할 수 있는 단점이 있다.
또한 리플렉션을 이용하면 코드가 지저분해지고 성능이 떨어진다.
상식적으로 생각해도 그냥 메서드를 호출하는 것보다 클래스를 찾아서 메서드 리스트를 받아서 찾아서 호출하는 식이 느리다.
리플렉션은 딱 인스턴스 생성에만 쓰고, 그렇게 생성된 인스턴스는 인터페이스나 상위클래스로 참조해 사용하면 좋다.
막상 예제를 테스트해보니 잘 안된다.
느낌만 이해하고 그것을 설명하면, 리플렉션으로 런타임중에 사용되는 어떤 클래스를 Class.forName("java.lang.String"); 처럼 클래스 이름으로 찾고, Class 객체의 getDeclaredConstructor()로 생성자를 찾아 .newInstance()로 인스턴스까지 생성을 한 후, 그 인스턴스를 가리킬 수있는 super 클래스 혹은 인터페이스로 참조하고 해당 기능을 직접사용하는 것이다.
10. 네이티브 메서드는 신중히 사용하라(아니 사용하지 말라)
- 네이티브 메서드는 잘 사용하기 아주 어렵다. 사용하지 말자.
GC가 자동으로 메모리 수거도 못하고 자바코드랑 네이티브메서드랑 접착 코드를 작성해야하는데 엄청 어렵고 자바의 장점인 플랫폼을 타지 않는다는 것이 사라지고 이식성이 떨어지는 단점들이 존재하니 일반적인 개발자는 사용하지 말자.
11. 최적화는 신중히 하라
- 최적화, 성능, 효율성을 강조하기 위해 설계를 바꾸지 말자
병목현상이 일어나는 부분은 분명히 해결해야할 숙제고 이런것들을 하지 말라는 게 아니다.
여기서 하지말라는 것은 성능때문에 기존의 좋은 프로그램 설계를 깨는 것이 오히려 위험하고, 성능이 좋지 않을 수 있다는 것이다.
또한 각각의 성능 최적화 시도를 했으면 전후로 성능을 측정해서 확실히 하는 버릇을 들이라는 내용이다.
12. 일반적으로 통용되는 명명규칙을 따르라
- 개발하면서 이게 제일 어려운것 같다.
1) min, max처럼 통용되는 약어는 약어로 쓰되 단어를 임의의로 줄이지 않는다.
2) 클래스나 인터페이스 첫 글자는 대문자로 쓴다. ex) String
3) 메서드와 필드는 첫 글자를 소문자로 쓴다. ex) getName()
4) 상수는 전체 대문자와 띄어쓰기는 언더바로 한다. ex) NEGATIVE_INFINITY
5) 타입매개변수는 한 문자로 표현하고 임의의 타입은 T, 컬렉션의 원소는 E, Map의 키와 값은 K, V, 예외는 X, 메서드의 리턴 타입은 R을 사용한다.
6) 멤버 속성을 반환하는 메서드는 get으로 시작하는 동사구로 짓는다. ex) getSize()
7) boolean 값을 반환하면 has나 is로 시작하는 메서드를 짓는다.
9) 다른 타입으로 변환할 때는 toArray등의 형태로 짓는다. ex) toJson()
10) 의미가 잘 드러날 수 있는 단어를 조합해 사용한다.
참고로 여기 변수명을 지어주는 사이트가 있다. 아주 유용하다.(https://www.curioustore.com/#!/)
참고 자료
도서 : 이펙티브 자바(effective java) 3판