LostCatBox

Java 배우기 CH12

Word count: 1.4kReading time: 8 min
2022/12/23 14 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)
  2. 2. 열거형(enum) (???)
  3. 3. 애너테이션이란?