LostCatBox

Java 배우기 CH12

Word count: 1.4kReading time: 8 min
2022/12/23 Share

지네릭스(Generics)

지네릭스(Generics)란?

  • 컴파일시 타입을 체크해 주는 기능(compile-time type check)
  • 객체의 타입 안정성을 높이고 형변환의 번거로움을 줄여줌

Untitled

  • 예시
    아래와 같은경우 컴파일러가 찾아내지못한다. ArrayList는 Object의 배열이므로, list.get(2)가 Object 객체를 반환하므로 이를 형변환 가능할것이라고 생각된다.
    하지만 실제 runtime exception이 발생한다. 이유는 실제로는 String 객체를 Integer 로 형변환이 불가능하기때문이다

Untitled

  • 이를 ArrayList<Integer> = new ArrayList<Integer>(); 로 제네릭을 사용한다면, 컴파일러가 오류를 찾아낼수있다.

Untitled

타입 변수

  • 지네릭 클래스를 작성할 떄, Object타입 대신 타입 변수(E)를 선언해서 사용
  • 원래는 일반클래스는 모두 Object타입을기반으로 생략되어있다.

Untitled

타입변수에 대입하기

  • 객체를 생성시, 타입 변수(E)대신 실제 타입(Tv)을 지정(대입)
  • 참조변수, 생성자의 타입변수등 모두 실제 타입으로 변경해줘야함.
  • 형변환 생략 가능하도록 메서드포함 모두 실제타입으로 변경되어 객체생성됨

Untitled

지네릭스 용어

  • Box
    지네릭 클래스, 클래스명 <타입변수> 형태
  • 타입 변수 또는 타입 매개변수 (T는 타입 문자)
  • Box
    일반 클래스, 원시 타입(raw type)

Untitled

지네릭 타입과 다형성

  • 참조 변수와 생성자의 대입된 타입은 일치해야한다.(무조건항상 타입은 일치해야한다
    혹시 다르게하고싶다면 와일드카드해야함)
  • 지네릭 클래스간의 다형성은 성립
    (대입된 타입은 무조건 일치해야함)
  • 매개변수의 다형성도 성립

Untitled

  • Object대신 타입변수덕분에 Product 타입이 들어갔고, 이는 get함수또한 Product타입으로 반환한다. 따라서 만약 Tv타입이 필요하다면 Tv tv = (Tv) g.get() 형변환 필수
  • List tvList = new ArrayList; 는 가능 타입일치, 다형성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
import java.util.*;

class Product {}
class Tv extends Product {}
class Audio extends Product {}

class Ex12_1 {
public static void main(String[] args) {
ArrayList<Product> productList = new ArrayList<Product>();
ArrayList<Tv> tvList = new ArrayList<Tv>();
// ArrayList<Product> tvList = new ArrayList<Tv>(); // 에러.
List<Tv> tvList = new ArrayList<Tv>(); // OK. 다형성

productList.add(new Tv());
productList.add(new Audio()); //다형성!! 적용됨.

tvList.add(new Tv());
// tvList.add(new Audio()); //안됨, Tv타입의 조상 자손관계아니므로

printAll(productList);
// printAll(tvLis t); // 컴파일 에러가 발생한다. 함수와 객체의 타입 변수가 다르다!!
// ArrayList<Product> = new ArrayList<Tv>() ;안되는원리와같다 //참조변수와 생성자 타입 불일치
}

public static void printAll(ArrayList<Product> list) {
for (Product p : list)
System.out.println(p);
}
}

Iterator

  • 클래스를 작성할 때, Oject타입 대신 T와 같은 타입 변수를 사용
  • 제네릭 이터레이터 사용시 형변환 필요없어진다
    (기존것은 Object반환하므로 형변환필요했음)

Untitled

HashMap <K,V>

  • 여러 개의 타입 변수가 필요한 경우, 콤마(,)를 구분자로 선언
  • 예시도 마찬가지로 형변환 불필요

Untitled

제한된 지네릭 클래스

  • extends로 대입할 수있는 타입을 제한(클래스, 인터페이스사용법동일)
  • 아래 예시는 T에 대입할수있는 타입은 Fruit의 자손만가능

Untitled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import java.util.ArrayList;

class Fruit implements Eatable {
public String toString() { return "Fruit";}
}
class Apple extends Fruit { public String toString() { return "Apple";}}
class Grape extends Fruit { public String toString() { return "Grape";}}
class Toy { public String toString() { return "Toy" ;}}

interface Eatable {}

class Ex12_3 {
public static void main(String[] args) {
FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
FruitBox<Apple> appleBox = new FruitBox<Apple>();
FruitBox<Grape> grapeBox = new FruitBox<Grape>();
// FruitBox<Grape> grapeBox = new FruitBox<Apple>(); // 에러. 타입 불일치
// FruitBox<Toy> toyBox = new FruitBox<Toy>(); // 에러.

fruitBox.add(new Fruit());
fruitBox.add(new Apple());
fruitBox.add(new Grape());
appleBox.add(new Apple());
// appleBox.add(new Grape()); // 에러. Grape는 Apple의 자손이 아님
grapeBox.add(new Grape());

System.out.println("fruitBox-"+fruitBox);
System.out.println("appleBox-"+appleBox);
System.out.println("grapeBox-"+grapeBox);
} // main
}

class FruitBox<T extends Fruit & Eatable> extends Box<T> {} //제한된 제네릭 클래스만

class Box<T> {
ArrayList<T> list = new ArrayList<T>();
void add(T item) { list.add(item); } //item을 저장할 list
T get(int i) { return list.get(i); } //박스에 item 을 추가
int size() { return list.size(); } //박스에 item 꺼낼
public String toString() { return list.toString();}
}

지네릭스의 제약

타입 변수에 대입은 인스턴스 별로 다르게 가능

  • static멤버에 타입 변수 사용 불가
    (static은 모든 인스턴스 공통이므로)
  • 배열 생성할때 타입 변수 사용불가. 타입 변수로 배열 선언은 가능
    (new T) 불가능

Untitled

와일드 카드 <?>

  • 하나의 참조 변수로 대입된 타입이 다른 객체를 참조 가능

Untitled

  • 메서드의 매개변수에 와일드 카드 사용

Untitled

지네릭 메서드

  • 지네릭 타입이 선언된 메서드(타입 변수는 메서드 내에서만 유효)
  • 클래스의 타입 매개변수 와 메서드의 타입 매개변수 는 별개

Untitled

  • 메서드를 호출할 때마다 타입을 대입해야 (대부분 생략 가능)

Untitled

지네릭 타입의 형변환

  • 지네릭 타입과 원시 타입 간의 형변환은 바람직 하지 않다.(경고 발생)
  • 권장하지않음!! 원시타입과 꼭 구별하여 사용하기

Untitled

  • 와일드 카드가 사용된 지네릭 타입으로는 형변환 가능

Untitled

지네릭 타입의 제거

  • 컴파일러는 지네릭 타입을 제거하고, 필요한 곳에 형변환을 넣는다( 결국 지네릭 타입은 없어진다!!!)

Untitled

열거형(enum) (???)

  • 관련된 상수들을 같이 묶어놓은것, Java는 타입에 안전한 열거형을 제공

Untitled

열거형의 정의와 사용

  • 열거형을 정의하는 방법

Untitled

  • 열거형 타입의 변수를 선언하고 사용하는 방법
  • 열거형 상수의 비교에 ==와 compareTo() 사용가능

Untitled

열거형의 조상 - java.lang.Enum

  • 모든 열거형은 Enum의 자손이며, 아래 메서드를 상속받는다.

Untitled

  • 예시
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
enum Direction { EAST, SOUTH, WEST, NORTH }

class Ex12_5 {
public static void main(String[] args) {
Direction d1 = Direction.EAST;
Direction d2 = Direction.valueOf("WEST");
Direction d3 = Enum.valueOf(Direction.class, "EAST");

System.out.println("d1="+d1);
System.out.println("d2="+d2);
System.out.println("d3="+d3);

System.out.println("d1==d2 ? "+ (d1==d2));
System.out.println("d1==d3 ? "+ (d1==d3));
System.out.println("d1.equals(d3) ? "+ d1.equals(d3));
// System.out.println("d2 > d3 ? "+ (d1 > d3)); // 에러
System.out.println("d1.compareTo(d3) ? "+ (d1.compareTo(d3)));
System.out.println("d1.compareTo(d2) ? "+ (d1.compareTo(d2)));

switch(d1) {
case EAST: // Direction.EAST라고 쓸 수 없다.
System.out.println("The direction is EAST."); break;
case SOUTH:
System.out.println("The direction is SOUTH."); break;
case WEST:
System.out.println("The direction is WEST."); break;
case NORTH:
System.out.println("The direction is NORTH."); break;
default:
System.out.println("Invalid direction."); break;
}

Direction[] dArr = Direction.values();

for(Direction d : dArr) // for(Direction d : Direction.values())
System.out.printf("%s=%d%n", d.name(), d.ordinal());
}
}

열거형에 멤버 추가하기

  • 불연속적인 열거형 상수의 경우, 원하는 값을 괄호()안에 적는다
  • 괄호()를 사용하려면, 인스턴스 변수와 생성자를 새로 추가해 줘야한다.
  • 열거형의 생성자는 묵시적으로 private이므로, 외부에서 객체생성 불가

Untitled

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
enum Direction2 { 
EAST(1, ">"), SOUTH(2,"V"), WEST(3, "<"), NORTH(4,"^");

private static final Direction2[] DIR_ARR = Direction2.values();
private final int value;
private final String symbol;

Direction2(int value, String symbol) { // 접근 제어자 private이 생략됨
this.value = value;
this.symbol = symbol;
}

public int getValue() { return value; }
public String getSymbol() { return symbol; }

public static Direction2 of(int dir) {
if (dir < 1 || dir > 4)
throw new IllegalArgumentException("Invalid value :" + dir);

return DIR_ARR[dir - 1];
}

// 방향을 회전시키는 메서드. num의 값만큼 90도씩 시계방향으로 회전한다.
public Direction2 rotate(int num) {
num = num % 4;

if(num < 0) num +=4; // num이 음수일 때는 시계반대 방향으로 회전

return DIR_ARR[(value-1+num) % 4];
}

public String toString() {
return name()+getSymbol();
}
} // enum Direction2

class Ex12_6 {
public static void main(String[] args) {
for(Direction2 d : Direction2.values())
System.out.printf("%s=%d%n", d.name(), d.getValue());

Direction2 d1 = Direction2.EAST;
Direction2 d2 = Direction2.of(1);

System.out.printf("d1=%s, %d%n", d1.name(), d1.getValue());
System.out.printf("d2=%s, %d%n", d2.name(), d2.getValue());
System.out.println(Direction2.EAST.rotate(1));
System.out.println(Direction2.EAST.rotate(2));
System.out.println(Direction2.EAST.rotate(-1));
System.out.println(Direction2.EAST.rotate(-2));
}
}

애너테이션이란?

  • 주석처럼 프로그래밍 언어에 영향을 미치지 않으며, 유용한 정보를 제공

표준 애너테이션

  • Java에서 제공하는 애너테이션

Untitled

@Override

  • 오버라이딩을 올바르게 했는지 컴파일러가 체크하게 한다
  • 오버라이딩할 때 메서드이름을 잘못적는 실수를 하는 경우가 많다
  • 항상 오버라이딩시에 선언부 위에 @Override 붙여주기
    (실수막아줌)

Untitled

@Deprecated

  • 앞으로 사용하지 않을 것을 권장하는 필드나 메서드에 붙인다.
  • @Deprecated의 사용예, Date클래스의 getDate()

Untitled

@FunctionalInterface

  • 함수형 인터페이스에 붙이면, 컴파일러가 올바르게 작성했는지 체크
  • 함수형 인터페이스에는 하나의 추상메서드만 가져야 한다는 제약이있음

@SuppressWarnings

  • 컴파일러의 경고메시지가 나타나지 않게 억제한다.
  • 괄호 () 안에 억제하고자하는 경고의 종류를 문자열로 지정
  • 지정한 경고는 나오지 않음

메타 애너테이션

  • 애너테이션을 위한 애너테이션
  • java.lang.annotation패키지 포함됨

Untitled

@Target

  • 애너테이션을 정의할 때, 적용대상 지정에 사용

Untitled

@Retention

  • 애너테이션이 유지되는 기간을 지정하는데 사용

Untitled

@Documented

@Inherited

@Repeatable

애너테이션 직접 만들기

넘어감

CATALOG
  1. 1. 지네릭스(Generics)
    1. 1.1. 지네릭스(Generics)란?
    2. 1.2. 타입 변수
      1. 1.2.1. 타입변수에 대입하기
    3. 1.3. 지네릭스 용어
    4. 1.4. 지네릭 타입과 다형성
    5. 1.5. Iterator
    6. 1.6. HashMap <K,V>
    7. 1.7. 제한된 지네릭 클래스
    8. 1.8. 지네릭스의 제약
    9. 1.9. 와일드 카드 <?>
    10. 1.10. 지네릭 메서드
    11. 1.11. 지네릭 타입의 형변환
    12. 1.12. 지네릭 타입의 제거
  2. 2. 열거형(enum) (???)
    1. 2.1. 열거형의 정의와 사용
    2. 2.2. 열거형의 조상 - java.lang.Enum
    3. 2.3. 열거형에 멤버 추가하기
  3. 3. 애너테이션이란?
    1. 3.1. 표준 애너테이션
    2. 3.2. @Override
    3. 3.3. @Deprecated
    4. 3.4. @FunctionalInterface
    5. 3.5. @SuppressWarnings
    6. 3.6. 메타 애너테이션
      1. 3.6.1. @Target
      2. 3.6.2. @Retention
      3. 3.6.3. @Documented
      4. 3.6.4. @Inherited
      5. 3.6.5. @Repeatable
    7. 3.7. 애너테이션 직접 만들기
      1. 3.7.1. 넘어감