Item52에서는 다중 정의를 신중히 사용하라 말한다 책의 나온 예제 코드만 보면 매개변수에 따라 다른 값이 출력될 것 같지만 실제로는 Collection 타입으로 인식되어 세 번 다 "Collection"만 나오게 된다 public class CollectionClassifier { public static String classify(Set s) { return "Set"; } public static String classify(List s) { return "List"; } public static String classify(Collection s) { return "Collection"; } public static void main(String[] args) { Collection[] col..
Item51에서는 메서드 시그니처를 신중히 설계하라 한다 규모가 큰 프로젝트를 진행할 때 절실히 깨달을 수 있다 개념적으로 유사한 행위를 하는 메서드의 이름이 다를 때, 하나하나 구현 부분을 찾아가 봐야 하고 어떤 기능을 하는지 뭐가 다르길래 이름의 뉘앙스가 다른지를 파악해야 한다 메서드를 작성하는 입장에서는 아주 명확하다고 생각하고 작성했더라도 메서드를 사용하는 입장에서도 과연 그런지 고민해볼 필요가 있다 나의 경험으로는 클린 코드를 읽고 서술적인 메서드명에 꽂혀서 표현력 있는 메서드를 작성했다고 뿌듯해했었는데 코드 리뷰에서 개념적 유사성을 고려한 메서드명을 지으라는 피드백을 받은 적이 있다 도메인 엔티티를 수정할 때, 어떤 메서드는 changeXX를 사용하고 어떤 메서드는 editXX을 사용했었다 작..
Item50에서는 적시에 방어적 복사본을 만들라 한다 defensive copy를 만들어야 하는 이유는 미성숙한 & 악의적인 클라이언트의 잘못된 접근을 막기 위함이다 secure coding의 한 종류로써 사용하는 쪽에서 무슨 짓을 하든 객체의 불변성, 안전성이 위협 받지 않도록 하는 것이다 상식적으로 보면 이런 일이 과연 일어날까 싶지만 대규모 프로젝트의 경우 초기 인원들이 끝까지 남아 소프트웨어 생명주기 전체를 관리하는 일은 흔치 않을 것이다 그렇다면 작성한 사람과 관리하는 사람이 다를 것이고 어떤 의도로 코드를 작성했을지 알 길이 없다 책에 등장하는 Date를 인스턴스 변수로 가지는 Period의 예로 보면 Date 자체가 불변을 보장하지 못 하는 클래스라 단순하게 Date에 setXX 메서드를 이..
item49는 매개변수가 유효한지 검사하라 말한다 메서드나 생성자에 적용해야 하는 규칙으로써 이를 지키지 않으면 실패 원자성을 깨부술 수 있기 때문이다 실패 원자성이란 쉽게 생각해서 잘못 됐을 때 바로 터져야 함을 의미한다 매개변수가 유효하지 않음으로 인해 바로 그 순간에 예외가 터지는게 아닌 로직을 타고 흘러 흘러 어딘지도 모를 엉뚱한 곳에서 예외가 터진다면 실패 원자성이 깨진 상태다 웹 개발을 하는 경우 입력 유효성을 하는 대표적인 상황은 Controller에서 매개변수를 받는 상황일 것이다 이 역시 여러 방법으로 처리가 가능한데 @Valid @RequestBody를 이용해 객체를 매핑 받아 사용하고 바인딩이 실패하는 경우, 실패 원인을 담고 있는 BindingResult도 받아서 추가 처리도 가능하..
Item48에서는 스트림 병렬화를 주의하라 말한다 병렬화란 단어에는 엄청난 성능 효과가 있을 것 같은 느낌이 든다 특히 자바는 컬렉션 등의 데이터 소스에 stream() 대신 parallelStream()으로 간단하게 병렬로 만들 수 있는 것이 함정이다 왜 함정인고 하니 병렬 스트림은 순차 스트림 보다 빠른 경우가 드물고 더 느린 경우가 많다 개념적으로 쉽게 와닿지 않을 수 있다 하나의 스레드가 큰 뭉탱이 하나 처리하는 것이 어떻게 여러 스레드가 작은 뭉탱이를 처리하는 것보다 느릴 수 있을까? 그 이유는 이전 글 Item45에 간략하게 정리해뒀다 [Item45] Stream vs For-Loop Item45에서 스트림은 주의해서 사용하라 말한다 Stream API는 다량의 데이터 처리 작업을 돕고자 Ja..
Item47에서는 반환 타입으로 스트림 보다 컬렉션이 낫다고 한다 개발을 하면서 스트림을 반환 타입으로 메서드를 작성한 적이 없어 문득 궁금해졌다 스트림 반환과 관련해서 밸덩과 스택오버플로우 글을 찾아봤다 - https://www.baeldung.com/java-return-stream-collection - https://stackoverflow.com/questions/24676877/should-i-return-a-collection-or-a-stream 컬렉션 반환은 data structure에 접근하는 것이고 스트림 반환은 data storage 안의 data source에 접근하는 것이다 어느 상황에 Stream을 반환해야 하는가에 대해서 스택오버플로우에 꽤나 정확한 답변이 있다 개발에서의 대..
Item46에서 스트림에는 부작용 없는 함수를 사용하라 말한다 스트림은 함수형 프로그래밍에 기초한 패러다임이라는데 그럼 함수형 프로그래밍이라는 패러다임은 무엇인가? 깔끔하고 유지 보수하기 쉬운 코드를 만들기 위한 코드 작성 방식이란다 OOP와 대치되는 개념이 아니고 둘이 짬뽕시켜 쓸 수 있다 함수형 프로그래밍은 모던 프로그래밍에서 대세가 되어가는 중인데 절차형, 객체지향, 함수형 중 함수형이 가장 먼저 나왔으나 그동안 많이 쓰이지 못했던 것은 불변 객체와 side-effect를 다루는 문제 때문이다 객체가 불변이라면 다른 값이 필요한 경우에 기존 객체를 변경할 수 없으니 항상 새로운 객체를 생성해야 한다 side-effect를 만들지 않으려면 외부 변수를 변경하지 못하고 모든 변경은 새로운 값으로 대체해..
Item45에서 스트림은 주의해서 사용하라 말한다 Stream API는 다량의 데이터 처리 작업을 돕고자 Java8부터 추가되었고 짜잘한 for, if문을 스트림으로 대체할 수 있다 Stream API의 핵심은 책을 참고해보면 다음과 같다 1. Stream 데이터 소스로부터 오는 유한 또는 무한의 흐름 2. Stream Pipeline 데이터 소스를 돌리면서 수행하는 연산의 단계 (중간 연산과 종단 연산으로 나뉜다) Stream이란 이름에서부터 알 수 있듯이 데이터가 흐르고 있고 중간 연산을 활용해 변환(mapping)하거나 걸러낸다(filtering) 쌩으로 출력하거나 다른 파일에 쓰는 경우가 아니라면 무언가 가공이 필요한 데이터이므로 중간 연산이 들어갈 것이다 파이프라인이라는 단어에서 유추할 수 있듯..
Item44는 표준 함수형 인터페이스를 사용하라 말한다 람다 등장 이전에 구현체마다 다른 행동을 지정하기 위해 사용하는 방법으로 템플릿 메서드 패턴이 있었다 상위 클래스의 메서드를 오버라이딩해 동작을 변경할 수 있는데 아래와 같은 형태다 public abstract class Animal { void yahoo() { System.out.println("YAHOO"); } } public class Cat extends Animal { @Override void yahoo() { System.out.println("CAT YAHOO"); } } public static void main(String[] args) { Animal animal = new Cat(); animal.yahoo(); } 실제 동..
Item43에서는 람다보다 메서드 참조를 사용하라 말한다 메서드 참조를 사용하는 이유는 간결함 때문이다 람다를 사용해 익명 함수를 대체하고 간결하게 표현하기로 한 거 끝까지 가보자는 것이다 메서드 참조를 사용하는 방법은 네 가지가 있다 1. static method reference 형태로 참조한다 2. instance method reference (bound receiver) 형태로 참조한다 3. instance method reference (unbound receiver) 메서드 형태로 참조한다 4. constructor reference 연산자 형태로 참조한다 이 중 한정적과 비한정적의 차이가 확 와닿지 않아서 좀 더 찾아봤다 책에서는 수신 객체를 특정하느냐 아니냐로 한정적과 비한정적 메서드 참..
Item42에서는 익명 클래스보다 람다를 사용하라 말한다 lambda expression은 자바 8부터 등장해 자바에서도 함수형 프로그래밍이 가능해지도록 본격적인 FP의 장을 열었다 자바8 이전에는 FP가 불가능했느냐 하면 그건 아니지만 욕먹어도 쌀 수준의 코드를 작성해야 했다 람다는 왜 나왔을까? 구현해야 할 코드가 단순하더라도 익명 클래스를 직접 구현하는 그 과정이 아주 지독하기 때문이란다 아래 코드에서 JDK 1.8 이전 방식을 보면 절로 한숨이 나온다 // JDK 1.8 이전 방식 Collections.sort(words, new Comparator() { @Override public int compare(String o1, String o2) { return Integer.compare(o1...
오랜만에 읽고 시작하자 https://www.baeldung.com/java-marker-interfaces 마커 인터페이스는 메서드나 상수 없이 런타임에 정보를 제공하기 위해 사용한다 컴파일러나 JVM이 그 정보를 바탕으로 특정 작업을 수행한다 이렇게만 들으면 이걸 어따 써? 싶은 생각이 드는데 실제적인 예로 마커 애노테이션의 대표로는 JUnit에서 사용하는 @Test, 마커 인터페이스는 Serializable, Closeable, Cloneable 등이 있다 마커 인터페이스의 유용성은 특정 작업을 수행하는 메서드 시그니처를 제대로 선언했을 때만 의미가 있다 ObjectOutputStream의 writeObject 메서드는 Object 타입의 인자를 받을 수 있게 작성되었다 런타임에 터질 에러를 컴파일..
Item40의 핵심은 @Override를 붙일 수 있는 상황에는 반드시 붙이라는 것이다 책에서는 equals 메서드를 재정의하면서 발생할 수 있는 오묘한 실수를 예로 들었다 면접 단골 질문이기도 한 Override와 Overload의 차이점을 체화하고 있다면 저지르지 않을 실수지만 때로는 알고 있어도 틀릴 때가 있으니 제약이 필요하다, 그 제약이 바로 @Override다 재정의와 다중 정의를 간단하게 짚고 넘어가자 - Override 시그니처가 완전히 같되, 공변 반환을 지원하기 때문에 반환 타입은 달라질 수 있다 - Overload 메서드 반환 타입, 이름은 같되 매개변수를 다르게 받아 같은 이름으로 다른 타입, 개수의 인자를 받을 수 있다 현대적인 IDE를 쓰고 있다면 애노테이션은 색 자체가 달라 애..
Item39는 명명 패턴보다 애노테이션을 사용하라 말한다 좁게 보면 명명 패턴에 해당하는 내용이지만 넓게 보면 직접 지정하지 말고 책임을 갖고 있는 도구에게 위임하라는 것이다 JUnit4 이전까지는 테스트 코드를 작성할 때 테스트 돌릴 메서드의 이름을 testXX처럼 지어야 했다고 한다 test로 시작한다고 해서 특별히 힘든 것은 아니다 다만 test로 시작하지 않으면 테스트가 돌아가지 않는 것이 문제다 JUnit4는 이 문제를 @Test 애노테이션으로 해결한다 @Test는 테스트용임을 표시하는 marker annotation이고 이를 처리하는 annotation processor가 따로 있다 애노테이션 프로세서가 없다면 무용지물이니 IDE 세팅에서 활성화 시키는 것이 필요하다 gradle을 사용하고 있..
자바의 enum은 타입 안전한 상수를 선언하기 위한 야무진 도구다 Java1.5부터 등장했고 상수 집합을 나타내는 특별한 데이터 타입이다 다만 모든 것이 일장일단이 있듯 enum도 상속할 수 없다는 단점이 존재한다 우선 왜 enum은 상속할 수 없는지 그 이유부터 알아보자 가능한 값들의 닫힌 집합을 만들기 위해서다 닫힌 집합이란 컴파일 시점에 가능한 값들을 모두 알기 위해서고 이 방식이 개발자나 컴파일러 모두에게 이롭다고 한다 확장이 가능하다면 메서드의 인자로 A라는 enum을 받는 경우 리스코프 치환 원칙에 따라 A를 상속한 B라는 enum도 받을 수 있어야 한다 이런 경우 switch-case에서 사용할 수 없다 - switch(A) 라고 선언했는데 런타임에 B가 들어올 수 있다면 모든 case에 대..
Item37은 enum에서 ordinal 사용을 자제하고 컬렉션을 이용하라는 것의 확장판이다 책에서 상전이를 예로 들었는데 OrdinalTransition은 가장 간단하게 생각할 수 있는 코드로 작성됐다 ordinal을 사용해 2차원 배열로 만들었고 예제에서는 상전이가 일어나지 않았을 때 null을 넣어줬지만 살짝 변경해서 NONE을 추가하고 null을 대체했다 반면 Transition은 Item37의 조언을 따라 ordinal을 사용하지 않고 코드가 꽤나 복잡해졌지만 enum의 values()를 이용하기 때문에 타입 안전하며 Collectors.groupingBy()와 중첩 맵을 활용 해 상전이를 나타냈다 뭐든 간에 depth가 깊어질수록 이해하기 쉽지 않은데 TRANSITION_MAP은 난해하기 그지..
Item36에서는 비트 필드 대신 EnumSet을 사용하라는데 그전에 비트 필드는 무엇일까? 메모리를 직접 다루지 않는 자바에서는 비트 연산을 수행할 일이 많지 않다 특히나 고수준 API를 사용하는 웹 개발이라면 더욱 그렇다 '아 요런 것이 있구나'와 '이게 뭔데'는 하늘과 땅 차이니 세부 구현까지는 아니더라도 요런 것이 있구나 수준까지는 알아보자 비트 필드에 대한 상세한 설명이 담긴 좋은 글을 찾았다 깊게 알아보고 싶다면 꼭 읽어보자, 한 번에 빡 이해하기는 힘들것 같고 주기적으로 봐줘야겠다 Bit Fields - Java - Languages - Programming - Computers Bit Fields A bit field is a memory-saving tool that was develop..
책에 나온 enum 사용의 안 좋은 예시를 가져왔다 public enum Ensemble { SOLO, DUET, TRIO, QUARTET, QUINTET, SEXTET, SEPTET, OCTET, NONET, DECTET; public int numberOfMusicians() { return ordinal() + 1; } } 왜 안 좋은 것인가? 열거 타입 상수와 연결된 위치 값을 반환하는 ordinal() 메서드를 사용하기 때문이란다 코드는 멋들어진 API를 사용해 걸작을 만들어내는 것보다 모르는 사람이 봐도 이해할 수 있도록 만드는 게 어렵다 numberOfMusicians() 메서드를 이해하기 위해서는 위치값에 1을 더해 반환하는데 ordinal()이 위치 값을 반환한다는 사실도 알아야 하고, ..
프로그래밍 세계에는 매직 넘버를 피하라는 격언이 있다 위 격언을 지키기 위해 Config 같은 설정 클래스에서 초기화 과정에 값을 넣어주는 경우를 제외하고, 재사용 가능성이 있는 상수라면 흔히들 아래와 같이 작성하기도 한다 public static final int APPLE_FUJI = 0; public static final int APPLE_PIPPIN = 1; public static final int ORANGE_FUJI = 0; public static final int ORANGE_PIPPIN = 0; 위처럼 표현할 시, 반복되는 prefix가 존재하고 더 심각한 문제는 타입 안전성이 보장되지 않는 것이다 위의 예시는 두 단어로 이어졌을 뿐이기에 가독성도 나쁘지 않고 그럭저럭 쓸 만 하지만 ..
Item33에서는 type safe heterogeneous container pattern에 대해 알아보는데 Parameterized Type Key를 넣는 게 과연 무슨 의미인가 궁금해졌다 너무 복잡한데 비해 실제로 만들어 본 상황이 거의 없으니 이걸 왜 쓰지? 라는 생각이 들었다 읽다보니 동적 형 변환에도 타입 안전성을 보장하기 위함이고 jackson과 같은 라이브러리에서 요긴하게 쓰고 있는 것 같다 기본적인 것들은 모두 이해했다고 가정하고 책의 예시에서 등장한 Favorites 클래스의 두 번째 제약에 대해 더 알아보려 한다 List과 같은 제네릭 타입은 실체화 불가 타입이다 즉 List.class 와 같이 쓸 수 없고 타입 안전 이종 컨테이너를 만들더라도 제네릭 타입은 사용 불가하다는 얘기다 이..