글

라벨이 Effective Java인 게시물 표시

(Effective Java) 익명 클래스보다는 람다를 사용하라_람다와 스트림

이미지
람다를 왜 JDK 1.1에서는 함수객체를 만드는 주요 수단은 익명 클래스였다. 그러나 익명클래스 방식은 코드가 너무 길어 자바는 함수형 프로그래밍에 적합하지 않았다. 결국 이러한 문제로 이를 람다로 교체됨 (더욱 간결하게 표현할 수 있음) 람다는 이름이 없고 문서화도 못 한다. 따라서 코드 자체로 동작이 명확히 설명되지 않거나 코드 줄 수가 많아지면 람다를 쓰지 말아야 한다. 람다는 한 줄일때 가장 좋고 길어야 세 줄안에 끝내는게 가장 좋다. 세 줄을 넘어가면 가독성이 심하게 나빠진다. 람다의 시대가 열리며 익명클래스 -> 람다로 대체 되었지만, 람다는 함수형 인터페이스에서만 쓰인다. 예컨대 추상클래스의 인스턴스를 만들 때 람다를 쓸 수 없으니, 익명 클래스를 써여한다. 마지막으로 람다는 자신을 참조할 수 없다. this키워드는 바깥 인스턴스를 가르킨다. 람다를 직렬화하는 일은 극히 삼가야한다. 정리: 함수객체를 구현하기 위해 익명클래스를 도입, 그러나 코드가 길어지는 단점이 존재하여 이를 람다로 대체하기 시작함 그러나 람다를 사용하지 못하는 경우가 있는데 1. 자기 자신을 참조하는 this를 사용하는 경우 2. 코드길이가 너무 길어지는 경우 오히려 문서화를 할 수 없음 3. 직렬화를 하는 경우는 삼가야 한다.

(Effective Java) 필요하다면 방어적 복사본을 만들어라 !!!!!!!!!!!!!!!!중요!!!!!!!!!!!!!!!!!!!!!!

이미지
->얼핏보면 변경이 불가능할것처럼 보이지만 Date start = new Date(); Date end = new Date(); Period p = new Period(start, end); end.setYear(78); 하지만 Date가 변경 가능 클래스라는 점을 이용하면 불변식을 깨뜨릴 수 있다. 따라서 Period객체의 내부를 보호하려면 생성자로 전달되는변경 가능 객체를 반드시 방어적으로 복사 그러나 Period p = new Period(start, end); p.end().setYear(78); //p의 내부를 변경하게 된다. -> 길이가 0 아닌 배열은 항상 변경 가능하다는 사실을 기억하자. 그러니 내부 배열을 클라이언트에게 전달할 때는 방어적 복사본을 만들어야 한다. 우리가 배워야 할 진짜 교훈은, 객체의 컴포넌트로는 가능하다면 변경 불가능 객체를 사용해야 한다는 것이다. 그래야 방어적 복사본에 대해서는 신경 쓸 필요가 없다는 것이다.

(Effective Java) 자료형을 정의할 때는 인터페이스를 사용하라

*표식 인터페이스는 아무 메소드도 선언하지 않는 인터페이스다. 클래스를 만들때 표식 인터페이스를 구현하는 것은 해당 클래스가 어떤 속성을 가지고 있다는것을 표시하는 것이다. 그 예로 Serializable이다. 그러면 여기서 궁금증이 생긴다. 표식 어노테이션을 사용하면 되는 것이 아닌가? 가장 중요한 첫번째 장점은, 표식 인터페이스는 결국 표식 붙은 클래스가 만드는 객체들이 구현하는 자료형이라는 점이다. 표식 어노테이션은 자료형이 아니다. 즉, 표식 인터페이스는 자료형이므로, 표식 어노테이션을 쓴다면 프로그램 실행 중에나 발견하게 될 오류를 컴파일 시점에 발견할 수 있다. 두번째 장점은, 적용 범위를 좀 더 세밀하게 지정할 수 있다는 것이다. 어노테이션 자료형을 선언할 때 target을 ElementType.TYPE으로 지정하면 해당 어노테이션은 어떤 클래스나 인터페이스에도 적용 가능하다. 그런데 특정한 인터페이스를 구현한 클래스에만 적용하고자한다면 표식인터페이스를 사용해야 한다.

(Effective Java) Override 어노테이션은 일관되게 사용하라

* 상위 클래스에 선언된 메소드를 재정의 할때는 반드시 선언부에 Override어노테이션을 붙여야 한다.

(Effective Java) 클래스와 인터페이스

*정보은닉이 중요한 이유는, 정보은닉이 시스템을 구성하는 모듈 사이의 의존성을 낮춘다.(decouple) * 각 클래스와 멤버는 가능한 한 접근 불가능하도록 만들라 * 객체 필드는 절대로 public으로 선언하면 안된다. * 변경가능 public 필드를 가진 클래스는 다중 스레드에 안전하지 않다. 변경 불가능 객체를 참조하는 final 필드라 해도 public 으로 선언하면 클래스의 내부 데이터 표현 형태를 유연하게 바꿀수없게된다. 공개 API의 일부가 되어버리므로, 삭제하거나 수정할 수 없게된다. 공개 API의 일부가 되어버리므로, 삭제하거나 수정할 수 없게 되는 것이다. 이 말은 static에도 해당된다. 단, 어떤 상수들이 클래스로 추상화된 결과물의 핵심적 부분을 구성한다고 판단되는 경우, 해당 상수들을 public static final 필드들로 선언하여 공개할수 있다. 이런 필드들은 대문자로 구성된 이름을 가지며, 이름을 구성하는 단어들은 밑줄 기호로 구분한다.

(Effective Java) clone을 재정의 할때는 신중하라

이미지
* Cloneable 인터페이스에는 clone 메소드가 없으며, Object의 clone 메소드는 protected로 선언되어 있다. *Cloneable 인터페이스에 아무런 메소드가 없다면 대체 Cloneable이 하는일은 무엇인가? ->protected로 선언된 Object의 clone 메소드가 어떻게 동작할지 정한다. -> 만일 어떤 클래스가 Cloneable을 구현한다면, Object의 clone메소드는 해당 객체를 필드단위로 복사한 객체를 반환한다. *일반적으로 인터페이스를 구현한다는 것은 클래스가 무슨 일을 할 수 있는지 클라이언트에게 알리는 것이다. 그런데 Cloneable의 경우에는 상위 클래스의 protected멤버가 어떻게 동작할지 규정하는 용도로 쓰이고 있다. *clone 메소드의 일반규약은 느슨하다. java.lang.Object 명세를 보면 다음과 같다. 객체의 복사본을 만들어서 반환하다. "복사"의 정확한 의미는 클래스마다 다르다. 일반적으로는 다음의 조건이 충족되어야 한다. 객체 x가 있다고 하자. -> x.clone() != x는 참이어야 한다. x.clone().getClass() == x.getClass() 는 참이 되겠지만, 반드시 그래야 하는것은 아니다. x.clone().equals(x)  이것도 실행한 결과가 true가 되겠지만, 반드시 그래야 하는것은 아니다. * 사실상 clone 메소드는 또 다른 형태의 생성자다. 원래 객체를 손상시키는 일이 없도록 해야하고, 복사본의 불변식(invariant)도 제대로 만족시켜야 한다. 만약 스택을 복사하려면 스택의 내부구조도 복사해야 한다. -> elements.clone() 호출 결과를 Object[] 로 형변환 할 필요가 없음에 유의하자. 릴리즈 1.5버전부터는 배열에 clone을 호출하면 반환되는 배열의 컴파일 시점자료형은 복제 대상 배열의 자료형과 같다. 위의 해법...

(Effective Java) toString은 항상 재정의하라

* toString 일반 규약에는 이런 구절이 있다. "모든 하위 클래스는 이 메소드를 재정의함이 바람직하다." * toString 메소드를 재정의하면 해당 객체만 혜택을 보는 것이 아니라 해당 객체에 대한 참조를 유지하는 객체들, 특히 컬렉션까지 혜택을 본다. 맵을 출력했을 때, "{Jenny=PhoneNumber@163b91}" 것보다 "{Jenny=(707)867-5309}" 이것이 나을 것이다. *가능하다면 toString 메소드는 객체 내의 중요정보를 전부 담아 반환해야 한다.

(Effective Java) equals를 재정의할 때는 반드시 hashCode도 재정의 하라

* equals를 재정의할 때는 반드시 hashCode도 재정의 해야 한다. 그렇지 않으면 Object.hashCode의 일반 규약을 어기게 되므로, HashMap, HashSet, HashTable같은 해시(hash) 기반 컬렉션과 함께 사용하면 오작동하게 된다. <Object 클래스 명세에서 복사해 온 일반 규약> 응용프로그램 실행 중에 같은 객체의  hashCode를 여러 번 호출하는 경우, equals가 사용하는 정보들이 변경되지 않았다면, 언제나 동일한 정수(Integer)가 반환되어야 한다. 다만 프로그램이 종료되었다가 다시 실행되어도 같은 값이 나올 필요는 없다. equals(Object)메소드가 같다고 판정한 두 객체의 hashCode값은 같아야 한다. equals(Object)메소드가 다르다고 판정한 두 객체의 hashCode값은 꼭 다를 필요는 없다. 그러나 서로 다른 hashCode값이 나오면 해시테이블(hashTable)의 성능이 향상될 수 있다. <이상적인 해시 함수를 만드는 방법> 1. 17과 같은 0 아닌 상수를 result라는 이름의 int 변수에 저장한다. 2. 객체 안에 있는 모든 중요필드 f에 대해서(equals 메소드가 사용하는 필드를 말한다) 아래의 절차를 시행한다. A. 해당필드에 대한 int 해시 코드 c를 계산한다. 필드가 boolean 이면(f?1:0)을 계산한다. 필드가 byte, char, short, int 중 하나면 (int)f 를 계산한다. 필드가 long이면 (int)(f^(f>>>32))를 계산한다. 필드가 float이면 Float.floatToIntBits(f)를 계산한다. 필드가 double이면 Double.doubleToLongBits(f)를 계산하고 그 결과로 얻은 long값을 위의절차 3에 따라 해시코드로 변환한다. 필드가 객체 참조이고 equals 메소드가 해당 필드의 equals메소드를...

(Effective Java) equals를 재정의 할 때는 일반 규약을 따르라

이미지
*  Object.equals 를 재정의 하는 것이 바람직할 때는 언제인가? -> 객체 동일성이 아닌 논리적 동일성의 개념을 지원하는 클래스 일 때,그리고 상위 클래스의  equals가 하위 클래스의 필요를 충족하지 못할 때 재정의 해야 한다. 값 클래스(value class)는 대체로 그 조건에 부합한다. 값 클래스는 Integer나 Date처럼, 단순히 어떤 값을 표현하는 클래스다. 값 객체에 대한 참조를 비교하는 프로그래머는 두 객체가 같은 값을 나타내는지를 알기 위해 equlals를 사용하지 , 동일한 객체인지 보려고  equals를 호출하지 않는다. <equals 메서드를 정의할 때 준수해야 하는 일반 규약(general Contract)> ->equals메소드는 동치 관계(equivalence relation)를 구현한다. 다음과 같은 관계를 동치관계라 한다. 반사성(reflexive) : null이 아닌 참조 x가 있을 떄, x.equals(y)는 true를 반환한다. 대칭성(symmetric) : null 이 아닌 참조 x와 y가 있을 때, x.equals(y) 는 y.equals(x)가 true일 때만 true를 반환한다. 추이성(transitive): null 이 아닌 참조 x,y,z가 있을때, x.equals(y) 가 true이고 y.equals(z)가 true이면 x.equals(z)도 true이다. 일관성(consistent): null이 아닌 참조 x와 y가 있을 때, equals를 통해 비교되는 정보에 아무런 변화가 없다면, x,.equals(y) 호출결과는 호출횟수에 상관없이 항상 같아야 한다. null이 아닌 참조 x에 대해서, x.equals(null)은 항상 false이다. ->이것은 CaseInsetiveString의 equals에서는 String객체에 대해서는 알지만 String의 equals메소드는  CaseInsen...