Note that this implementation throws an
36 | * {@code UnsupportedOperationException} <
4 |
5 | ### 결론
6 | - 인터페이스는 자신을 구현한 클래스의 인스턴스를 참조할 수 있는 타입 역할을 한다.
7 | - 인터페이스는 오직 이 용도로만 사용하자!
8 |
9 |
10 | ### ***안티패턴 : 상수 인터페이스***
11 | ```java
12 | public interface LottoNumbersConstants{
13 | static final int LOTTO_NUMBER = 6;
14 | static final int LOTTO_MIN = 1;
15 | static final int LOTTO_MAX = 45;
16 | }
17 | ```
18 |
19 | **왜 쓰면 안될까?**
20 | - 클래스 내부에서 사용하는 상수는 내부 구현에 해당함. 그러므로 상수 인터페이스를 구현하는 것은 내부 구현을 클래스의 API로 노출하는 행위! (사용자에게 의미없는 정보를 노출하여 혼란을 야기)
21 | - 내부 코드가 상수들에 종속되게 된다.
22 | - 이후 상수가 필요없어져도 호환성 유지를 위해 여전히 상수 인터페이스를 사용해야할수도 있다
23 |
24 | 실제 자바 플랫폼 라이브러리에서도 잘못 활용한 예가 존재하긴 하다.
25 |
26 | 
27 |
28 | 이는 잘못 활용한 것이니 절대 따라하지 말자.
29 |
30 | **상수를 나타내기 위한 방법?**
31 | - 특정 클래스나 인터페이스와 강하게 연관된 상수라면 그 클래스나 인터페이스 자체에 추가해주기
32 | - 열거 타입으로 나타내기 적합한 상수라면 Enum 타입으로 만들어 공개하기 (item34)
33 | - 인스턴스화할 수 없는 유틸리티 클래스에 담아 공개하기 (item4)
34 |
35 | > 인터페이스는 타입을 정의하는 용도로만 쓰자. 상수쓰려고 쓰지 말자! 🙅
--------------------------------------------------------------------------------
/handbook/04/23. 태그 달린 클래스보다는 클래스 계층구조를 활용하라.md:
--------------------------------------------------------------------------------
1 | # 태그 달린 클래스보다는 클래스 계층구조를 활용하라.
2 |
3 | - 핵심 : 추상클래스를 잘 활용해!
4 |
5 | ```
6 | // 태그 달린
7 | public class Figure {
8 |
9 | enum Shape {RECTANGLE, CIRCLE};
10 |
11 | final Shape shape;
12 |
13 | double length;
14 | double width;
15 |
16 | double radius;
17 |
18 | public Figure(double length, double width) {
19 | this.shape = Shape.RECTANGLE;
20 | this.length = length;
21 | this.width = width;
22 | }
23 |
24 | public Figure(double radius) {
25 | this.shape = Shape.CIRCLE;
26 | this.radius = radius;
27 | }
28 |
29 | double area() {
30 | switch (shape) {
31 | case RECTANGLE:
32 | return length * width;
33 | case CIRCLE:
34 | return Math.PI * (radius * radius);
35 | default:
36 | throw new AssertionError(shape);
37 | }
38 | }
39 | }
40 | ```
41 |
42 | - 태그 달린 클래스의 단점
43 | - 가독성이 떨어진다.
44 | - 코드가 변경에 취약하고 오류를 내기 쉽다.
45 | - 사실상 안쓰는게 맞다.
46 |
47 | ```
48 | // 추상클래스
49 | abstract class Figure {
50 | abstract double area();
51 | }
52 |
53 | class Circle extends Figure {
54 | final double radius;
55 |
56 | public Circle(double radius) {
57 | this.radius = radius;
58 | }
59 |
60 | @Override
61 | double area() {
62 | return Math.PI * (radius * radius);
63 | }
64 | }
65 |
66 | class Rectangle extends Figure {
67 | final double length;
68 | final double width;
69 |
70 | public Rectangle(double length, double width) {
71 | this.length = length;
72 | this.width = width;
73 | }
74 |
75 | @Override
76 | double area() {
77 | return length * width;
78 | }
79 | }
80 | ```
81 | - 추상클래스의 장점
82 | - 사실상 태그달린 클래스와 같은 컨셉이지만 단점은 없고 장점은 더 많다.
83 | - 없어도 되는 필드는 없게 만들수 있기 때문에 간결하다.
84 | - area() 메서드에서 오류가 발생할 가능성이 낮고 코드의 유지보수성이 좋아진다.
85 | - 상속 받은 클래스는 더 자유롭게 확장할 수 있다.
86 |
--------------------------------------------------------------------------------
/handbook/04/24. 멤버 클래스는 되도록 static으로 만들라.md:
--------------------------------------------------------------------------------
1 | # 아이템 24. 멤버 클래스는 되도록 static으로 만들라
2 |
3 | ## 중첩 클래스란?
4 |
5 | 중첩 클래스란 다른 클래스 내부에 정의된 클래스를 말한다.
6 | - 두 클래스의 멤버들을 쉽게 접근 가능
7 | - 외부로부터 불필요한 관계 클래스를 감춤으로써 코드의 복잡성 감소
8 |
9 | ### 중첩 클래스의 종류
10 |
11 | 
12 |
13 | - static으로 선언한 멤버 클래스를 static 중첩 클래스라고 부른다.
14 | - 정적 멤버 클래스
15 | - non-static 멤버 클래스를 내부 클래스라고 부른다.
16 | - 비정적 멤버 클래스
17 | - 익명 클래스
18 | - 지역 클래스
19 |
20 | ## 정적 멤버 클래스
21 |
22 | 정적 멤버 클래스는 바깥 클래스 내부에 static으로 선언된 클래스이다.
23 | - 바깥 클래스의 `private` 멤버에도 접근 가능
24 | - 흔히 바깥 클래스와 함께 쓰일 때만 유용한 public 도우미 클래스로 쓰인다.
25 |
26 | ```java
27 | public class Person {
28 |
29 | private final String name;
30 | private final int age;
31 | private final int weight;
32 | private final int height;
33 |
34 | public static class Builder {
35 | private final String name;
36 | private final int age;
37 |
38 | private int weight = 0;
39 | private int height = 0;
40 |
41 | public Builder(String name, int age) {
42 | this.name = name;
43 | this.age = age;
44 | }
45 |
46 | public Builder weight(int val) {weight = val; return this;}
47 |
48 | public Builder height(int val) {height = val; return this;}
49 |
50 | public Person build() {
51 | return new Person(this);
52 | }
53 | }
54 |
55 | private Person(Builder builder) {
56 | name = builder.name;
57 | age = builder.age;
58 | weight = builder.weight;
59 | height = builder.height;
60 | }
61 | }
62 | ```
63 | ```java
64 | Person JJANG9 = new Person.Builder("짱구", 7)
65 | .weight(80).height(200).build();
66 | ```
67 |
68 | ## 비정적 멤버 클래스
69 |
70 | 비정적 멤버 클래스란 바깥 클래스 내부에 static 키워드 없이 선언된 클래스이다.
71 | - 바깥 인스턴스 없이는 존재할 수 없다.
72 | - 인스턴스 메서드에서 정규화된 this를 사용해 바깥 인스턴스의 메서드를 호출한다거나 바깥 인스턴스를 참조할 수 있다.
73 |
74 | ```java
75 | public class Outer {
76 | String name = "짱구";
77 |
78 | public void run() {
79 | Inner inner = new Inner();
80 | inner.run();
81 | }
82 |
83 | public class Inner {
84 | public void run() {
85 | System.out.println(Outer.this.name);
86 | }
87 | }
88 | }
89 | ```
90 |
91 | ## 결론
92 |
93 | **멤버 클래스에서 바깥 인스턴스에 접근할 일이 없다면 무조건 static을 붙여서 정적 멤버 클래스로 만들자.**
94 | - static을 생략하면 바깥 인스턴스로의 숨은 외부 참조를 갖게 되고 심각한 경우 GC가 바깥 인스턴스를 수거하지 못하는 경우가 발생한다. (메모리 누수)
95 |
96 | ## 익명 클래스
97 |
98 | 익명 클래스는 사용 시점에 인스턴스화 되어 한번만 사용할 수 있게 만든 클래스이다.
99 | 익명이라 이름이 없기 때문에 생성자를 가질 수 없고 단 하나의 클래스를 상속받거나 하나의 인터페이스만 구현 가능하다.
100 | - 익명 클래스에 새롭게 정의된 필드와 메서드는 익명 클래스 내부에서만 사용 가능하다.
101 | - 람다가 나오면서 익명 클래스는 빠이~
102 |
103 | ```java
104 | public class Anonymous {
105 | public void show() {
106 | }
107 | }
108 |
109 | public class Main {
110 | public static void main(String[] args) {
111 | Anonymous anonymous = new Anonymous() {
112 | public void hide() {
113 | System.out.println("는 못말려!");
114 | }
115 |
116 | @Override
117 | public void show() {
118 | System.out.print("짱구");
119 | hide();
120 | }
121 | };
122 |
123 | //anonymous.hide(); 호출 불가
124 | anonymous.show();
125 | }
126 | }
127 | ```
128 |
129 | ## 지역 클래스
130 |
131 | 지역 클래스는 가장 드물게 사용되는 중첩 클래스이다.
132 | - 접근 제한자(public, private) 및 static을 붙일 수 없다.
133 | - 메서드 내부에서만 사용되기 때문에 접근을 제한할 필요가 없다.
134 | - 정적 멤버(정적 필드와 메서드)를 가질 수 없다.
135 |
136 | ```java
137 | public class OuterJJANG9 {
138 | private String outerMessage = "짱";
139 |
140 | public void method() {
141 | class LocalJJANG9 {
142 | public void print() {
143 | String localMessage = "구";
144 | System.out.println(outerMessage + localMessage);
145 | }
146 | }
147 |
148 | LocalJJANG9 localJJANG9 = new LocalJJANG9();
149 | localJJANG9.print();
150 | }
151 | }
152 | ```
153 |
--------------------------------------------------------------------------------
/handbook/04/25. 톱레벨 클래스는 한 파일에 하나만 담기.md:
--------------------------------------------------------------------------------
1 | # [아이템 25] 톱레벨 클래스는 한 파일에 하나만 담기
2 |
3 | ### 개요
4 |
5 | - 소스 파일 하나에 클래스를 여러개 선언하더라도 자바 컴파일러는 문제가 없음
6 | - `장점이 없으며`, `심각한 위험` 발생 가능
7 | - 한 클래스를 여러가지로 정의할 수 있으며, `어느 소스 파일을 먼저 컴파일하냐에 따라 실행 결과가 달라짐`
8 |
9 | ### Main.java
10 |
11 | ```java
12 | public class Main {
13 | public static void main(String[] args) {
14 | System.out.println("maple: " + MapleStory.INFORMATION + ", lostark :"
15 | + LostArk.INFORMATION);
16 | }
17 | }
18 | ```
19 |
20 | ### LostArk.java
21 |
22 | ```java
23 | class MapleStory {
24 | public static String INFORMATION = "bad";
25 | }
26 |
27 | class LostArk {
28 | public static String INFORMATION = "good";
29 | }
30 | ```
31 |
32 | ### MapleStory.java
33 |
34 | ```java
35 | class MapleStory {
36 | public static String INFORMATION = "good";
37 | }
38 |
39 | class LostArk {
40 | public static String INFORMATION = "bad";
41 | }
42 | ```
43 |
44 | - javac [Main.java LostArk](http://Main.java)[.java](http://Dessert.java)
45 | - `MapleStory 클래스를 선언해주지 않은 상황`에서 Main.java에서 `MapleStory.INFORMATION`을 먼저 사용하므로 `MapleStory.java`를 먼저 살펴본다.
46 | - 이후 LostArk.java를 인자로 같이 넘겨주는데, Main.java에서 가져온 `MapleStory.java의 LostArk, MapleStory 클래스`와 `LostArk.java의 LostArk, MapleStory 클래스명이 중복`되므로 오류가 난다고 함 → 책에선 에러로 표기되나 실제 테스트 과정에서는 에러가 아닌 `LostArk.java를 사용`하는걸 확인
47 | - javac LostArk.java Main.java → “maple : bad, lostark : good” 출력
48 | - LostArk.java의 MapleStory, LostArk 클래스를 사용하게 된다.
49 | - javac Main.java LostArk.java MapleStory.java
50 | - 중복 클래스 선언 오류(duplicate class) → 컴파일이 되지 않음.
51 |
52 | ---
53 |
54 | ### 결론
55 |
56 | - 컴파일러에 `어느 소스코드를 먼저 넘겨주냐에 따라 동작이 달라지므로` 해결해야 하는 문제이다.
57 | - 해결책은 `톱레벨 클래스들을 서로 다른 소스 파일로 분리`해 준다.
58 | - (MapleStory.java에는 MapleStory 클래스만, LostArk.java에는 LostArk 클래스만)
59 | - 굳이 `여러 톱레벨 클래스를 한 파일에 담기를 원한다면 정적 멤버 클래스를 고민`해보는게 좋다.
60 |
61 | ```java
62 | public class Main {
63 | public static void main(String[] args) {
64 | System.out.println("maple: " + MapleStory.INFORMATION + ", lostark :"
65 | + LostArk.INFORMATION);
66 | }
67 |
68 | public static class MapleStory {
69 | public static String INFORMATION = "good";
70 | }
71 |
72 | public static class LostArk {
73 | public static String INFORMATION = "good";
74 | }
75 | }
76 | ```
77 |
78 | - ~~사실 IDE에서 대부분 잡아줌.~~
79 |
80 | ### 정리
81 |
82 | - 즉, `소스 파일 하나에는 반드시 톱레벨 클래스(or 인터페이스)를 하나만` 담아라.
83 |
--------------------------------------------------------------------------------
/handbook/05/26. 로 타입은 사용하지 말라.md:
--------------------------------------------------------------------------------
1 | # 26. 로 타입은 사용하지 말라
2 |
3 | ### 제네릭 타입(제네릭 클래스, 제네릭 인터페이스)
4 |
5 | ```java
6 | List