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이 그렇다, HashSetequals()를 사용하지만, TreeSetcompareTo()를 사용하여 값을 비교한다, 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로 선언하라
0
이전 댓글 보기
등록
TOP