Effective Java Review 01
Oct 03, 2019 조회수 153
Effective Java
근래 Effective Java를 읽고있다.
아쉽게도 가지고 있는 책은 2판이고 Java 1.7
기준으로 쓰여져있다 :'(
(최근 3판은 1.7
, 1.8
, 1.9
기반으로 쓰여져있다.)
책을 읽으면서, 이미 알게 모르게 그렇게 사용하는 문법?도있고
아, 이렇게 써야되는구나라는 부분도 있다.
이 책의 구조는 다음과 같이 이루어져있다.
처음에는, 이렇게 쓰는게 더 좋을걸?
다음에는, 왜 이렇게 써야되는지 보여줄게..^^
따라서 감명깊게 읽은 부분, 간략히, 위 구조로 정리하려고한다.
<br>
<br>
본격 정리글!
1 . 생성자 대신에 정적팩토리 메소드 패턴을 쓰는건 어때 ,? 고려하는것도 좋아
public static Boolean valueOf(boolen b){
return b ? Boolean.TRUE : Boolean.False;
}
- 생성자랑 달리 이름을 쓸 수 있다 - 예를 들어 , 생성자의 인자값을 엄청 구분해서 생성자가 많다고 생각해보자 , 극한으로 , 생성자가 100개가넘는다 . . . 그럼 각 생성자를 쓰기에 클라이언트 코드는 어렵다 . 근데 정적 팩토리 메소드는 메소드 이름을 줄 수 있다 , 좀 더 명시적이다 , 가독성 짱짱 !
- 자원을 재활용 할 수 있다 - 위에 예제를 보자. 굳이 새로 객체 생성안하고 기존 객체를 재활용 할 수 있다.
- 반환되는 리턴값을 좀 더 유연하게 사용 할 수 있다- 생성자는 그 객체 밖에 리턴 못한다 . 하지만 정적팩토리를 사용하면 리턴값을 하위클래스로 바꿀 수 있다 , 유연하다
- 단점일수도? 생성자가 없고, 정적 팩토리 메소드 패턴만 쓴다면 - 이런 경우는 하위클래스를 만들 수 없다
- 단점일수도? 일반 메소드랑 구분이 안 간다 - 이것이 생성자를 대신한 매소드인가 , static 메소드인가 구분이 가지 않는다 . 그래서 주석을 달거나 , 팩토리 메소드 이름을 지을때 조심해야한다 . 많이 쓰이는 것들 -
valueOf
,of
,getInstance
,newInstance
<br>
<br>
2. 생성자 인자가 많을때는 , Builder 패턴을 쓰자
- 인자수가 많다고생각해보자 - 클라이언트 코드의 가독성이 떨어진다 , 필더 패턴을 이를 해결 할 수있다 .
- 그럼 기본생성자에, Setter를 쓰자고? - 한번의 호출로 생성 할 수 없으므로 , 일시적으로 객체의 일관성이 깨져버린다 . 필더 패턴은 이를 해결한다
- 그러니 Builder 패턴을 쓰자!!
Lombok
을 씁시다 (feat.@Builder
)
<br>
<br>
3. 유효기간이 지난 객체는 참조를 폐기하자
public Object pop(){
return elements[--size]
}
- 위 예제의 문제가 뭘까 - 스택에서 Pop으로 꺼낸 A객체를 , 스택내부에서 참조값을 지워주지 않는다 .
- 그럼 무엇이 문제냐 - 스택애서 꺼낸 A객체를 꺼내서 다 쓰고 더 이상 참조 하지 않는다고 가정해보자 , A객체는 가비지 컬렉터가 처리해갈까?
- 그렇지 않다 - 스택내부에서 아직 참조하고 있으므로 , 가비지 컬렉터는 A객체를 수거해가지 않는다 .
- 그러니 꼭 객체 참조를 NULL처리해야 한다고 - 그렇게 강박할 필요는 없다 . 코드가 난잡해질 수가 있다 .
- 그럼 어느 경우에 - 자체적으로 메모리를 관리하는 객체에서 주의하자 . Stack은 자체적으로 배열이라는 메모리를 관리한다 , 캐싱도 마찬가지이다 , 이런 부분은 메모리 누수가 흔히 발생되는 부분이다
- 그럼 어느 경우에 - 리스너도 마찬가지이다 , 안쓰는 리스너는 참조값을 제거하지않는다면 가비지 컬렉터가 수거해가지 않을 것이다 !
<br>
<br>
4. Equals & HashCode 이야기
Equlas
를 정의하는거는 까다롭다- 보통 객체는
eqauls()
를 재정의 하지않고, 따라서 자기자신하고만 같다. - 굳이 한다면 객체가 동일한지를 보는게 아닌, 이 객체가 논리적으로 동일한가를 정의할때 사용할 것이다
- 다음 사항을 주의하자
- 반드시 세 가지를 규약을 지키자 - 반사성 , 대치성 , 추이성 세가지이다 , 쉽게 설명하면 다음과같다 .
- 반사성
A eq A = true
- 대칭성
A eq B = true, B eq A = true
, - 추이성
A eq B = true, B eq C = true, A eq C = true
이어야한다. - 상속을 받아 필드 추가한 하위클래스와 상위클래스간에
Equals
를 정의하지마라 - 애초에 둘은Equals
규약을 어기지 않을 수가 없다. Equals
인자에Null
이 들어가면다면 - 무조건false
를 리턴하자.- 먼저 자신과 같은 객체인지를 검사하자 -
==
인자를 사용하여 자신과 같은값인지 검사하자, 같을 경우 불필요한 로직을 줄일 수 있다. InstanceOf
를 확인하여 자료형을 검사하자 - 그렇지 않다면 , 하위 클래스가 아니니false
이다. 또한 애초에Equals
의 인자는 동일한 자료형으로 오는것이 대부분이다.eqauls()
인자를Object
에서 다른것으로 바꾸지말자 - 좀더 강력한 자료형검사를 위해 인자를 바꾸어 , 새로 메소드를 정의히자말자 , 굳이 그럴 필요 없다Object.equals()
를 재정의 하도록하자Equals
를 재정의할때는 반드시Hashcode()
또한 재정의하자 -eqauls
같으면hasecode
도 같아야 하는 규약을 준수하자.hasecode()
를 단순하게 정의하지말자 - 모든 객체가 하나의 해쉬 값을 값을 가진다고 생각해보자 , 한 버킷에 모든 객체가 들어가고 , 해쉬테이블은 결국 연결리스트가되서 성능이 느려진다. (이 부분은 hash 동작 과정을 참고하기바람 )
<br>
<br>
5. Clone을 재정의 할때는 신중하라 ( eq 쓰지마 . . )
- 쓰지마세요 -
Clonable
인터페이스를 구현받아clone
을 재정의해서 사용하지말자, 책에는 권장하지 않는다고 써있지만, eq 사용하지 말자 정도로 읽을 수 있다. - 왜? - 간단히 보면은 , 객체안에 객체를 참조하는 경우 , 또 그 객체에 객체를 참조하는 경우를 보자 , 그런 내부객체들 까지 모두 주소복사가 아닌 값복사가 되어야한다 . 또한 상위 클래스의
super.clone()
또한 모두 완벽히 재정의 되어 있어야한다. - 굳이 쓴다면, -
clone()
메소드를 재정의하지말고, 동일한 객체를 인자를 받는 생성자를 (PodoClass(PodoClass podo)
) 사용하거나newInstance(동일객체)
를 사용해 복사하도록 하자.super.clone()
보다는 낫지 않은가.
<br>
<br>
6. compareTo 이야기
Comparable
인터페이스에 정의되어 있다.equals
와 동일하게 세가지 규약을 지키자 (동치성, 대칭성, 추이성)- 동치성 -
A compare A = 0
- 대치성 -
A compare B > 0 , B compare A < 0
- 추이성 -
A compere C < 0, B compare C < 0, A compare C < 0
- 그리고 한 가지 규약이 더 있다
A compare B = 0, A eq B = true
이다 - 하지만 이규약은 권고 사항이지 필수 사항은 아니다 . 하지만 가능한 규약을지키자 .- 한 예로
BigDecimal
이 그렇다,HashSet
은equals()
를 사용하지만,TreeSet
은compareTo()
를 사용하여 값을 비교한다,new BigDecimal(1,0)
과new BigDecimal(1.00)
을 을HashSet
에 넣어보자, 두 객체가 삽입된다, 하지만TreeSet
에 삽입한다면, 하나의 객체만 삽입된다.(덮어씌움)
<br>
<br>
7. 변경가능성을 최소화하라 ,
본 책의 내용을 보면, 저자는 변경 가능성을 최소화하는것 중요시 함을 느낄 수 있다.
public void add(MyNumber myNumber){
return new MyNumber(this.value + myNumber.getValue());
)
public static final
불변 객체만 사용하자 -public static final Object[] arrays;
가 불변하다고 할 수 있을까?, 누군가 자유롭게 접근해서 배열의 값을 바꾼다면, 변경가능성이 다분하다, 배열뿐만아니라 객체 또한 마찬가지이다.setter()
를 제공하지말자 - 모든 필드에 대하여setter()
를 제공하지 말자. 필요한 부분에 수정자를 허용하자 (feat.setter()
메소드를 쓰기보다는, 상황에 맞추어 메소드를 명명해서 사용하자,setEnabled(false)
보다는,disabled()
를 쓰는 것이 좋다.)- 가능하면 객체, 필드에
final
키워드를 사용하자 - 계승 할 수 없을 명시하자,
final class
를 사용하자 - 변경불가능한, 불변객체를 사용하자
- 왜? - 변경 불가능한 객체는 아주 단순하다 .
- 왜? - 우선 변경 불가능한 객체는 쓰레드 세이프 할 수 밖 없다 , 각 쓰레드가 변경을 할 수 없기에 안전하다 , 그렇기에 자유롭게 사용할 수 있다 .
- 왜? - 변경 불가능한 객체는 내부도 공유 할 수 있다 . 내부를 변경 할 수 없다고 가정해보자 . 내부의 값이 변동되지 않으니 , 다른 객체에서 내부의 값을 똑같이 공유하여도 안전하다 .
- 왜? - 그러니까 안전하다 , 안전이 짱이지 .
- 단점? - 변경 불가능한객체의 유일한 단점은 별도의 객체를 계속 만드는 것이다 . 따라서 객체 생성비용이 높다는 것이다 . 위의 코드로 더하기를 만번하다고 가정해보자 , 10000개의 객체가 생성된다 , 이 문제에 해결책은 하나를 예로들면 아주 쉽게 해결 할 수 있다 .외쳐
StringBuilder
!!! - 결론 - 변경 할 이유가 없으면 가능하면 , 변경불가능한 객체로 만들어라 . 특별한 이유가없다면 final로 선언하라
'Effective Java Review 01' 관련된 다른글
이 포스팅은 쿠팡 파트너스 활동의 일환으로, 이에 따른 일정액의 수수료를 제공받습니다.