Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# 아이템 25. 톱레벨 클래스는 한 파일에 하나만 담으라

## 핵심 정리

- `한 소스 파일에 톱 레벨 클래스를 여러 개 선언하면 컴파일 순서에 따라 결과가 달라질 수 있다.`

```java
// 코드 25-1 두 클래스가 한 파일(Utensil.java)에 정의되었다. - 따라 하지 말 것! (150쪽)
class Utensil {
static final String NAME = "pan";
}

class Dessert {
static final String NAME = "cake";
}
```

- `다른 클래스에 딸린 부차적인 클래스는 정적 멤버 클래스로 만드는 것이 낫다. 읽기 좋으며 private으로 선언해서 접근 범위도 최소한으로 관리할 수 있다.`

```java
// 코드 25-3 톱레벨 클래스들을 정적 멤버 클래스로 바꿔본 모습 (151-152쪽)
public class Test {
public static void main(String[] args) {
System.out.println(Utensil.NAME + Dessert.NAME);
}

private static class Utensil {
static final String NAME = "pan";
}

private static class Dessert {
static final String NAME = "cake";
}
}
```
197 changes: 197 additions & 0 deletions docs/아이템 26. 로 타입은 사용하지 말라.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
# 아이템 26. 로 타입은 사용하지 말라

## 핵심 정리

```java
public class GenericBasic {

public static void main(String[] args) {
// Generic 사용하기 전
List numbers = new ArrayList(); // List는 로 타입
numbers.add(10);
numbers.add("whiteship");

for (Object number: numbers) {
System.out.println((Integer)number);
}

// Generic 등장 이후
List<Integer> nuberms = new ArrayList<>();
nuberms.add(10);
nuberms.add("whiteship");

for (Integer number: nuberms) {
System.out.println(number);
}
}
}
```

- `로 타입: List`
- `제네릭 타입: List<E>`
- `매개변수화 타입: List<String>`
- `타입 매개변수: E`
- `실제 타입 매개변수: String`
- `한정적 타입 매개변수: List<E extends Number>`
- `비한정적 와일드카드 타입: Class<?>`
- 아무런 타입을 받을 수 있다.
- `한정적 와일드카드 타입: Class<? extends Annotation>`

```java
public class Box<E> {

private E item;

private void add(E e) {
this.item = e;
}

private E get() {
return this.item;
}

public static void main(String[] args) {
Box<Integer> box = new Box<>();
box.add(10);
System.out.println(box.get() * 100);

printBox(box);
}

private static void printBox(Box<?> box) {
System.out.println(box.get());
}

}
```

- Box<Integer>는 컴파일 시점에 타입이 사라진다.
- Box의 타입을 Object(로 타입으로)로 꺼내고 Integer로 타입 캐스팅 수행

`매개변수화 타입을 사용해야 하는 이유`

- `런타임이 아닌 컴파일 타임에 문제를 찾을 수 있다. (안정성)`
- `제네릭을 활용하면 이 정보가 주석이 아닌 타입 선언 자체에 녹아든다. (표현력)`
- `“로 타입”을 사용하면 안정성과 표현력을 잃는다.`
- `그렇다면 자바는 “로 타입”을 왜 지원하는가?`
- 위 Box 예제에서 알 수 있듯이 컴파일러가 로 타입으로 꺼내는 이유는 하위 호환성을 유지하기 위해서이다.
- `List와 List<Object>의 차이는?`

```java
// 코드 26-4 런타임에 실패한다. - unsafeAdd 메서드가 로 타입(List)을 사용 (156-157쪽)
public class Raw {
public static void main(String[] args) {
List<String> strings = new ArrayList<>();
unsafeAdd(strings, Integer.valueOf(42));
String s = strings.get(0); // 컴파일러가 자동으로 형변환 코드를 넣어준다.
}

private static void unsafeAdd(List list, Object o) {
list.add(o);
}
}
```

- String의 리스트는 Object의 리스트가 아니기 때문에 List<Object>로 선언하면 컴파일 에러가 발생한다.
- 즉, 배열을 **공변**이지만 제네릭은 **불공변**
- **공변**: 타입 A가 B의 하위 타입일 때, Foo<A>는 Foo<B>의 하위 타입인 경우
- **불공변**: 타입 A가 B의 하위 타입일 때, Foo<A>는 Foo<B>의 하위 타입이 아니다.
- `Set과 Set<?>의 차이는?`

```java
public class Numbers {

static int numElementsInCommon(Set<?> s1, Set<?> s2) {
int result = 0;
for (Object o1 : s1) {
if (s2.contains(o1)) {
result++;
}
}

return result;
}

public static void main(String[] args) {
System.out.println(Numbers.numElementsInCommon(Set.of(1, 2, 3), Set.of(1, 2)));
}
}
```

- Set과 같이 사용하면 컬렉션의 안정성이 깨진다.
- Set<?>는 아무런 타입, 어떤 한 종류를 다루는 타입을 사용한다는 의미
- 로 타입(Set)은 안정적이지 않고 어떠한 값이던 넣을 수 있지만, Set<?>의 경우 안정성이 보장이되고 Set<?>로 선언한 경우 null 이외의 값을 넣을 수 없다.
- `예외: class 리터럴과 instanceof 연산자`

```java
public class UseRawType<E> {
private E e;

public static void main(String[] args) {
// UseRawType<String>.class 이러한 클래스는 존재하지 않는다.
// 컴파일 시점에 소거되기 때문에
// 반드시 로 타입의 클래스만 존재한다.
System.out.println(UseRawType.class);

UseRawType<String> stringType = new UseRawType<>();

System.out.println(stringType instanceof UseRawType);
// 컴파일은 되지만 소거되기 때문에 의미없다.
//System.out.println(stringType instanceof UseRawType<String>);
}
}
```

- 클래스 타입은 로 타입의 클래스만 존재한다.
- 컴파일 시점에 타입은 소거되기 때문이다.

## 완벽 공략

- `p156, 마이그레이션 호환성을 위해 로 타입을 지원하고 제네릭 구현에는 소거 방식을 사용하기로 했다. (아이템 28)`
- `p158, 제네릭 메서드 (아이템 30)`
- `p158, 한정적 와일드카드 타입 (아이템 31)`
- `Generic DAO 만들기`

### 완벽 공략 40. GenericRepository

`자바 Generic을 활용한 중복 코드 제거 예제`

```java
public class AccountRepository {

private Set<Account> accounts;
...
}

public class MessageRepository {

private Set<Message> messages;
...
}
```

```java
public class GenericRepository<E extends Entity> {

private Set<E> entities;

public GenericRepository() {
this.entities = new HashSet<>();
}

public Optional<E> findById(Long id) {
return entities.stream().filter(a -> a.getId().equals(id)).findAny();
}

public void add(E account) {
this.entities.add(account);
}
}

// 하위 클래스
public class GenericMessageRepository extends GenericRepository<Message> {
}

public class GenericAccountRepository extends GenericRepository<Account> {
}
```
83 changes: 83 additions & 0 deletions docs/아이템 27. 비검사 경로를 제거하라.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
# 아이템 27. 비검사 경로를 제거하라

## 핵심 정리

- `“비검사 (unchecked) 경고”란?`
- `컴파일러가 타입 안정성을 확인하는데 필요한 정보가 충분치 않을 때 발생시키는 경고.`

```java
public class SetExample {

public static void main(String[] args) {
Set names = new HashSet(); // 컴파일 경고!

Set<String> strings = new HashSet<>();
}
}
```

- java7 부터 다이아몬드 연산자를 생략해도된다.
- `Set<String> names = new HashSet<String>()` 와 같이 타입을 반복하지 않아도 된다는 의미 new HashSet<>()
- `할 수 있는 한 모든 비검사 경고를 제거하라.`
- `경고를 제거할 수 없지만 안전하다고 확신한다면 @SuppressWarnings(“unchecked”) 애노테이션을 달아 경고를 숨기자.`
- `@SuppressWarnings 애너테이션은 항상 가능한 한 좁은 범위에 적용하자.`
- `@SuppressWarnings(“unchecked”) 애너테이션을 사용할 때면 그 경고를 무시해도 안전한 이유를 항상 주석으로 남겨야 한다.`

```java
public class ListExample {

private int size;

Object[] elements;

public <T> T[] toArray(T[] a) {
if (a.length < size) {
/**
* 이 애노테이션을 왜 여기서 선언했는지..
*/
@SuppressWarnings("unchecked")
T[] result = (T[]) Arrays.copyOf(elements, size, a.getClass());
return result;
}
System.arraycopy(elements, 0, a, 0, size);
if (a.length > size)
a[size] = null;
return a;
}
}
```

- 내가 이미 알고 있는 경고라면 @SuppressWarnings를 선언해서 경고를 없애주자.

## 완벽공략

### 완벽 공략 41. 애너테이션

`자바 애너테이션을 정의하는 방법`

- `@Retention: 애노테이션의 정보를 얼마나 오래 유지할 것인가.`
- `Runtime, Source, Class`
- `@Target: 애노테이션을 사용할 수 있는 위치.`
- `Type, Field, Method, Parameter, …`

```java
@Retention(RetentionPolicy.SOURCE)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface MyAnnotation {
}
```

- `@Documented`: javadoc 생성시 해당 애너테이션를 포함해서 생성하게 한다.
- Retention.Source로 선언하게되면 리플랙션, 바이트코드 조작을 통해 정보를 가져오는 것이 불가하다.
- 그 외 Runtime, Class는 리플랙션, 바이트코드 조작으로 정보를 가져오는 것이 가능하다는 의미

```java
@MyAnnotation
public class MyClass {

@MyAnnotation
public static void main(String[] args) {
Arrays.stream(MyClass.class.getAnnotations()).forEach(System.out::println);
}
}
```
10 changes: 10 additions & 0 deletions src/main/java/item25/Dessert.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package item25;

// 코드 25-2 두 클래스가 한 파일(Dessert.java)에 정의되었다. - 따라 하지 말 것! (151쪽)
//class Utensil {
// static final String NAME = "pot";
//}
//
//class Dessert {
// static final String NAME = "pie";
//}
8 changes: 8 additions & 0 deletions src/main/java/item25/Main.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package item25;

// (150쪽)
public class Main {
public static void main(String[] args) {
System.out.println(Utensil.NAME + Dessert.NAME);
}
}
16 changes: 16 additions & 0 deletions src/main/java/item25/Test.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package item25;

// 코드 25-3 톱레벨 클래스들을 정적 멤버 클래스로 바꿔본 모습 (151-152쪽)
public class Test {
public static void main(String[] args) {
System.out.println(Utensil.NAME + Dessert.NAME);
}

private static class Utensil {
static final String NAME = "pan";
}

private static class Dessert {
static final String NAME = "cake";
}
}
10 changes: 10 additions & 0 deletions src/main/java/item25/Utensil.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package item25;

// 코드 25-1 두 클래스가 한 파일(Utensil.java)에 정의되었다. - 따라 하지 말 것! (150쪽)
class Utensil {
static final String NAME = "pan";
}

class Dessert {
static final String NAME = "cake";
}
22 changes: 22 additions & 0 deletions src/main/java/item26/genericdao/Account.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package item26.genericdao;

public class Account implements Entity {

private Long id;

private String username;

public Account(Long id, String username) {
this.id = id;
this.username = username;
}

@Override
public Long getId() {
return this.id;
}

public String getUsername() {
return username;
}
}
Loading