varargs란 메서드 인수의 개수를 클라이언트 측에서 결정할 수 있다는 점에서 개꿀이다 인수를 여러 개 넘기면 내부적으로는 배열을 만들어 담아두고 사용한다 바로 이 부분에서 문제가 되는데 가변 인수를 제네릭 타입으로 넘긴다면 실체화되지 않는 타입으로 배열을 만들기 때문에 타입 안전성이 보장되지 않아 possible heap pollution 경고가 뜬다 매개변수 타입을 변환하지 않고 사용한다면 문제없을 수 있으나 만약 얘를 메서드 내부에서 형 변환하거나 다른 타입을 참조하는 경우엔 ClassCastException이 터지게 된다 힙 오염의 의미는 아래 블로그 글을 참고해보자 아래의 예시는 로 선언해놓고 Double 타입의 요소를 갖고 있어 힙 오염이 발생했다 이 전에 제네릭과 배열을 언급했을 때는 아예..
Generics를 사용하면서 재사용성을 높이기 위한 방법으로 이름조차 버거운 한정적 와일드카드 타입이 있다 한정적 와일드카드 타입을 이해하기에 앞서 Generics의 불공변을 이해하기 위해 List과 List을 살펴보자, 둘은 어떠한 관계도 없는 다른 타입이다 List에는 기본형을 제외한 모든 객체 타입을 넣을 수 있는데 List에는 오직 String 타입만 넣을 수 있다 책에서는 상하위 관계에서 지켜져야 할 리스코프 치환 원칙을 지키지 않기 때문에 둘은 다른 타입이라 말한다 따라서 논리적으로도 맞고 타입 안전한 개발이 가능하지만 아쉬운 점이 있다 Generics은 정말 좋지만 타입 매개변수에 직접 타입을 지정해버리면 그 타입에 한정되어 굳어버린다 사용하려는 매개변수 타입 개수 만큼의 메서드를 만들어야 ..
객체지향의 핵심은 캡상추다로 요약된다고 말한 적이 있다 캡슐화로 내부 구현을 감추어 외부에서 내부 구현에 얽매이지 않게 하고 추상화를 통해 OCP를 준수해 변경에는 닫혀있고 확장에는 열린 포인트를 만들어준다 sub-class가 아닌 sub-typing을 통해 타입 계층을 만들어 전략 패턴 사용이 가능하게 하여 다형성을 이루어낸다 결국 객체지향의 최종 목표는 재사용성 향상과 유지보수성 향상을 위함이다 객체지향의 목표를 이루려면 클래스 설계, 컴포넌트 설계뿐만 아니라 메서드 단위에서의 재사용성도 중요하다 쉽게 말해 범용적인 메서드를 만들어야 여러 곳에서 활용이 가능하다는 말이다 정렬 메서드나 컬렉션 두 개를 합쳐주는 메서드를 사용할 때 하나의 메서드로 여러 곳에서 활용할 수 있다면 편리할 것이다 이런 상황에..
개발자라면 응당 '이왕이면 다홍치마' 대신 '이왕이면 제네릭이다'라고 말해야 한다 왜 제네릭인가? 런타임에 터질 ClassCastException을 컴파일 시점에 에러로 띄워주기 때문이다 일반 클래스를 type-safe 하도록 제네릭 클래스를 만들려면 타입 매개변수를 주면 된다, Stack -> Stack 단 이 클래스 내에서 배열을 사용한다면 컴파일 에러나 비검사 형 변환 경고가 뜨는데 이를 막기 위한 방법으로 두 가지가 있다 1. 주어진 타입으로 배열을 만드려고 할 때 Object[]로 만들고 그 후에 타입을 사용해 명시적인 형 변환을 때려준다 단 컴파일러는 런타임에 해당 라인이 안전함을 보장할 수 없기 때문에 개발자가 안전함을 확실하게 알 때만 사용해야 한다 이를 무시하면 런타임에 터질 에러를 막아..
Generics는 raw-type Collection, 배열을 사용할 때의 문제를 해결하기 위해 나왔다, 그 문제가 어떤 문제일까? 바로 Runtime 시 발생할 수 있는 ClassCastException을 컴파일 에러로 잡아준다는 것이다 알고리즘 문제를 풀 때는 보통 Collection 보다 배열을 쓰게 되는데 성능 차이가 꽤 크기 때문이다 이와 반대로 웹 개발에는 배열을 쌩으로 쓰는 일은 흔치 않고 컬렉션을 사용한다 성능 차이가 나는데도 컬렉션을 쓰는 이유는 컬렉션과 배열의 차이로 기인한다 컬렉션을 사용할 때, 주로 raw-type을 사용하지 않고 Generics와 버무려서 사용하는데 Generics는 invariant, 불공변이다 제네릭을 사용하는 경우 다른 타입으로 지정하면 상속 관계와 상관없이 ..
이전 아이템에서 Generics를 꼭 써야 한다는 것을 배웠다 다만 어떤 기술이든, 어떤 언어든 무언가를 도입함에 따라 장단점이 따라오게 되어있다 새로운 기술은 도입할 때의 장단점을 저울질해 이전의 장단점들을 상쇄하고 남을 효용이 생기면 도입해야 한다 Generics 장점으로 type-safety를 지켜주어 번거로운 형 변환이 필요 없고 잘못된 값이 들어갈 일이 없다는 점이 있다 단점으로 수 많은 unchecked-call이 뜰 수 있다는 것인데 잘 생각해보면 이건 장점이다 사용자 입장에서 타입 체크를 제대로 안 했기 때문에 친절한 IDE가 컴파일 전에 미리 뿜어내 주는 것이다 IDE에서 문법 오류나 구식 for문을 향상된 for문으로 바꿔주는 것과 같은 자동 수정은 지원하지 않으나 비검사 경고가 뜬다는..
옛날 옛적, Generics 도입 이전엔 List, Set, Map 같은 컬렉션에 특정 타입이고 나발이고 아무거나 집어넣고 멍 때리던 시절이 있었다 Java5부터 type-safety를 지원하려 구원자 Generics가 오셨다 raw type 을 쓰는 게 왜 문제인가? 당연 타입 안정성을 지원해주지 못하기 때문이다 Generics로 타입을 명시하지 않은 경우 기본적으로 컬렉션에 들어가는 요소를 Object로 간주한다 어떤 값을 넣든 Object 형태로 받아버리기 때문에 막상 꺼내서 쓰려고 할 때 Object로 사용할 것이 아니라면 원래 넣은 형태로 캐스팅을 해줘야 한다 1~2개 사용하고 개발자가 타입을 꿰차고 있는 경우엔 아무 상관없다만 조금 더 현실적으로 생각해보면 말 같지도 않은 상황이 펼쳐지는 것이..
톱 레벨 클래스는 한 파일에 하나만 담으라는데 이 말을 듣기 전에도 무의식적으로 수행하고 있었다 얼마 전 Kotlin in action을 깔짝 보고 top-level function을 써봤는데 Item25를 읽으면서 자바는 왜 안 되는지 궁금해졌다 그 이유는 자바의 초기 설계와 관련이 깊은데 자바에서는 모든 것이 객체다 다른 말로 하자면 모든 것이 객체 안에 있어야 한다 그렇기 때문에 톱 레벨에는 클래스를 두고 그 안에 변수나 메서드를 둬야 한다 비록 그 클래스가 아무런 의미 없이 컨테이너 역할을 하더라도 말이다 코틀린에서는 아마 이런 불필요한 껍데기를 두는 게 꼴 뵈기 싫어서 톱 레벨에 변수나 함수를 선언할 수 있도록 만든 거 같은데 JVM 언어이고 자바와의 상호 운용성을 제일로 치기 때문에 컴파일 시..
멤버 클래스를 왜 static으로 만들어야 하는가, 그전에 멤버 클래스는 왜 필요한가? 책에서는 멤버 클래스를 가지고 있는 클래스에서만 쓰여야 한다고 한다 Oracle 문서에서는 다음과 같이 설명한다 한 곳에서만 쓰일 녀석들을 논리적으로 묶기 위함 & 캡슐화, 가독성, 유지보수성 향상 즉 outer class 와 함께 쓰일 때만 의미 있는 클래스로써 도우미 역할을 한다 예를 들어 outer class에서 쓰일 상수 클래스가 있을 때, outer class 외에 쓰일 일이 전혀 없으면 top-level로 빼기엔 애매하다 더욱이 유지 보수를 위해서 개념적으로 가깝다면 물리적으로 가까이 두는 것이 좋다 이런 경우 inner class로 두면 응집력이 높고 결합도가 낮은 형태로 만들 수 있다 중첩 클래스엔 네 ..
요즘 내가 자주 시도하는 객체지향적 사고는 어떻게 하면 공통된 부분을 뽑아내 여러 곳에서 재사용할 수 있을 지다 작성한 지 꽤 지났는데 어떻게 공통된 부분을 뽑아냈고 이를 재사용할 수 있는지 고민했던 글이다 고수가 보면 콧방귀를 뀔 코드일 수 있으나 초보라면 참고해보면 괜찮을 거라 생각해 첨부한다 [OAuth2] SpringBoot OAuth2 적용기 & 깨지는 테스트 살리기 개인 프로젝트에 OAuth2를 적용해봤다 Spring Security를 이용한 로그인, 회원가입 과정을 대체하기 위해서는 아니고 학습 목적이다 학습 목적이라 해보고 싶은 것 다 하려고 google, facebook, github, naver, ryumodrn.tistory.com 저 코드도 아쉬운 부분이 많다 switch 난사로 더..
Interface는 언제 그리고 왜 쓸까? 행동에 제약을 걸기 위한 추상화로만 사용하자 책의 예제는 상수 인터페이스를 보여주는데 얼핏 보면 괜찮은 생각처럼 보일 수도 있다 상수 인터페이스를 구현한 모든 클래스에서 이 상수들을 이용하면 편할 거 같은데 두 가지 문제가 있다 첫번째는 타입 정의를 위해 사용해야 할 인터페이스를 올바르게 사용하지 않은 것이다 관례와 규칙을 따르지 않은 방식이므로 본인은 편하게 사용할지 몰라도 해당 코드를 본 대부분의 사람은 의문을 품게 될 것이다 둘째, 바이너리 호환성 인터페이스에서 상수를 공개했으므로 이를 사용하는 쪽에서 상수에 강하게 결합된다 상수를 수정할 일이 있을 때 인터페이스에서만 바꾸고 끝이 아니라 클라이언트 코드까지 전부 컴파일 해줘야 한다 게다가 인터페이스에서 상..
당연한 말이지만 인터페이스는 구현하는 쪽을 생각해 설계해야 하고 클래스는 사용하는 쪽을 생각해 설계해야 한다 코드를 작성한다는 것은 작게 보면 어떤 기술을 사용해 멋진 프로그램을 만들어내느냐 지만 크게 보면 결국 요구사항을 충족시키는 프로그램을 어떻게 만들어내느냐 이기 때문이다 클라이언트 혹은 액터라고 부르는 이해 관계자는 인터페이스나 클래스에 기대하는 행위가 있다 개발자는 그 기대에 부응할 의무가 있다 Java8부터 인터페이스에 default method를 추가할 수 있게 됐다, 이는 엄청난 축복이지만 저주가 될 수도 있다 위에서 말한 내용 때문인데 클라이언트는 인터페이스에 기대하는 행위가 있는데 수많은 구현체들에서 이 기대가 올바르게 작동하리라는 보장이 없기 때문이다 책에서는 apache.common..
Item20의 핵심은 한 줄로 요약된다 자바에서는 다중 상속이 되지 않기 때문에 추상 클래스보다는 인터페이스를 사용해야 한다 다중 상속을 허용하게 되면 다이아몬드 상속 문제를 피할 수 없다 그럼 다이아몬드 상속 문제가 무엇일까? 아하 Super1, Super2를 다중 상속받았을 때 어떤 놈의 메서드가 우선순위를 가져야 하는지 정하지 못하기 때문이로구나 어떤 계층이 위에 있는지, 어떤 놈이 먼저 나오는지 ex) extends Super1, Super2 이면 Super1의 메서드 상속 식으로 풀어도 되긴 하지만 결국 복잡성이 올라가는 문제를 안고 있다, 애초에 될 수 없게 하는게 간단하다 그럼 여러개를 가질 수 있는 방법이 없단 말인가? 다중 상속은 안 되지만 다중 구현은 가능하다 implements Sup..
Item19는 일반 개발자에게는 생소한 내용이 담겨있다, 물론 나에게도 그렇다 작성하는 클래스가 상속용인지 아닌지 결정하라는 것인데 이 것이 중요한 이유는 라이브러리 사용자에게 상속했을 때 신경 써야 할 부분을 알려주기 위함이다 이전 아이템에서 나온 HashSet 상속 시 발생할 수 있는 문제는 명세를 꼼꼼히 살펴봤다면 일어나지 않았을 것이다 이전 아이템은 아래 글 참고 [Item18] extends 멈춰 자바에서 extends를 이용해 상위 클래스를 상속 받으면 코드 복붙이 필요 없다 상위 클래스의 private 변수, 메서드를 제외한 모든 속성을 내려주기 때문이다 이런 편리함을 제공하는데도 상속 보다 ryumodrn.tistory.com Javadoc까지 완벽하게 작성할 것이 아니라면 @implSpe..
자바에서 extends를 이용해 상위 클래스를 상속 받으면 코드 복붙이 필요 없다 상위 클래스의 private 변수, 메서드를 제외한 모든 속성을 내려주기 때문이다 이런 편리함을 제공하는데도 상속 보다는 합성을 이용하란다, 왜일까? 어허어허, 구현 상속은 캡슐화를 깨트려서고만 protected는 내부 구현을 감춰뒀다는 환상을 준다고라 캡슐화의 핵심은 이전에 제공했던 public interface 대로 동작만 가능하면 아무 걱정 없게 해주기 때문이구만 만약 변경이 필요하면 인터페이스를 구현한 클래스에서만 신경 써주면 되니까 편하겠구먼 그런데 상속을 사용해 protected로 내려주면 상속 받은 녀석들까지 모두 신경 써야 하니까 귀찮아지고 이는 캡슐화가 깨졌음을 의미하는 것이로구나 OOP에서 복잡성을 관리하..
가변과 불변에 대한 좋은 글이 있다, 읽고 시작하자 Just Enough FP: Immutability Kyle Shevlin is a software engineer who specializes in JavaScript, React and front end web development. kyleshevlin.com 객체를 가변과 불변으로 만드는 절대적 기준은 딱히 없고 어떤 부분에 중점을 둘 것인지에 따라 달렸다 즉 애플리케이션을 만들 때 모든 객체를 가변으로 만들어도 되고 불변으로 만들어도 된다 다만 그 대가로 극심한 난이도의 유지보수성이나 지하로 곤두박질 친 성능이 따라올 수 있다 가변 객체는 귀요미 프로젝트일 땐 상관 없지만 며칠에 걸쳐 소스를 봐도 구조를 파악할 수 없을 때 문제된다 프로그램 작..
객체지향의 달인이 되려면 뭐든 간에 잘 숨겨야 한다, 객체지향을 처음 배울 땐 이 개념에 의문이 들었다 어차피 외부에서 쓰려고 만드는 건데 왜 숨겨야 하는가? 결합도를 낮추고 응집도를 높이기 위해서라는 말로는 이해하기 부족했다 불변 객체가 아니라면 인스턴스 변수들은 final이 아닐 것이고 변경 가능하다 접근 제한자까지 public이라면 어디서든 값을 변경할 수 있다 이는 엄청난 자율성을 주지만 시간이 지나면 참혹한 대가가 따른다 H/W, S/W 성능 향상으로 인해 변하지 않을 코드는 없다 이 정도면 기가 막히구만 하고 짰더라도 다음 버전에서 그 성능을 한참 앞지르는 알고리즘이 나올 수도 있고 절차 지향에서 객체지향으로, 객체지향에서 리액티브로 패러다임이 바뀌어버릴 수도 있다 가장 흔한건 역시 클라이언트..
객체지향의 핵심은 풀어 설명하면 한도 끝도 없지만 간단하게 보면 캡상추다로 요약된다 1. 캡슐화 2. 상속 3. 추상화 4. 다형성 이 중 캡슐화에 해당하는 내용이 Item15에 나오는데 외부에서의 접근 권한을 최소화하는 것이다 개인적으로는 컴포넌트의 손발 자르기라고 생각해 불호에 가깝지만 기술적으로 보면 맞는 말이기 때문에 따라야 한다 프로그램 유지보수의 난이도는 변경이 어디까지, 얼마나 전파되느냐에 따라 달라지기 때문이다 변경의 영향을 최소한으로 줄이기 위해 setter를 막아두고 특별한 이름의 메서드를 만들어 사용해야 한다 사실 setter를 열어두거나 필드를 public으로 열어두면 유연성이 향상된다 혼자 만드는 프로젝트라면야 세터를 열어두든 퍼블릭으로 다 열어두든 누가 뭐라하겠는가? 특히 내가 ..
Item14의 핵심은 작성하는 클래스가 순서가 필요하다면 Comparable을 구현하라는 것이다 모던 자바 인 액션에서 스트림의 다양한 사용법을 배우는데 그때 Comparable을 사용하는 코드도 많다 당시엔 단순 사용법만 익히느라 어떤 원리로 돌아가는지 몰랐는데 이번에는 깊게 읽어봤다 왜 Comparable을 구현하라고 했을까? 보통 알파벳, 숫자, 연대 같이 순서를 가진 값 클래스를 이용하는 과정에 유용하게 쓰일 수 있기 때문이다 책에서는 전화번호를 지역번호, 앞자리, 뒷자리로 잘라서 표현하는 클래스를 작성하는데 비지니스 로직에 따라 지역번호 별 정렬, 앞자리 별 정렬 등이 필요한 경우가 있을 수 있다 또한 자연적인 순서를 가지고 있다면 Arrays.sort()를 이용해 간단히 정렬시킬 수도 있다 C..
Cloneable은 mixin interface라고 한다, 믹스인 인터페이스가 무엇일까? Item20에서 자세히 설명되어 있는데 A라는 클래스가 있다면 Cloneable과 같은 mixin interface를 구현하게 하면 A 클래스가 가진 본연의 기능에 다른 기능을 덧붙이기 때문에 (mix-in) 믹스인이라고 한다 두 번째 코멘트를 보자 자바에서는 인터페이스에 상태나 코드를 가질 수 없어 믹스인 인터페이스란 건 없다고 하며 추상 클래스를 이용했다면 다중 상속을 지원하지 않기에 모든 조건을 만족할 수 없다고 했다 자바8 이후로 인터페이스에서 메서드 구현이 가능하므로 이제는 믹스인 인터페이스가 존재할 수 있다 그럼에도 Cloneable은 정상적인 믹스인 인터페이스라고 할 수 없는데 clone() 메서드를 직..