티스토리 뷰

Java

[Generices] 지네릭스

GOMSHIKI 2023. 10. 24. 23:37

 

 

1.1 지네릭스란?

다양한 타입의 객체들을 다루는 메서드나 컬렉션 클래스에 Compile 시 타입 체크를 해주는 기능!

지네릭스의 장점
1. 타입의 안정성을 제공
2. 타입체크와 형변환을 생략할 수 있으므로 코드가 간결해짐

 

 

 

1.2 지네릭 클래스의 선언

class Box(

	Object item;
    
    void setItem(Object item) { this.item = item; }
    Object getItem() { return item; }
    
)

 

위 클래스를 지네릭 클래스로 변경하면 다음과 같이 클래스 옆에 '<T>'를 붙이고, 'Object'를 모두 'T'로 바꾼다.

class Box<T> { // 지네릭 타입 T를 선언
	
    T item;
    void setItem(T item) { this.item = item; }
    T getItem() { return item; }

}
Box<T>           :  타입 변수(Type Variable) T는 'Type'의 첫 글자
ArrayList<E>  :  타입 변수(Type Variable) E는 'Element'의 첫 글자
Map<K, V>     :  타입 변수가 여러개인 경우 콤마(,) 로 구분, K는 'Key'(키)를 의미하고 V는 'Value'(값)을 의미
* 상황에 맞게 사용!

 

* 예시

Box<String> b = new Box<String>(); 	// 타입 T 대신, 실제 타입을 지정
b.setItem(new Object());		// 에러. String 이외의 타입은 지정할 수 없음!!
b.setItem("ABC");			// OK. String 타입이므로 지정 가능!!
String item = (String) b.getItem();	// 형변환 불필요

/**
* 지네릭 클래스 Box<T> 정의
*/
class Box {
	
    String item;
    void setItem(String item) { this.item = item; }
    String getItem() { return item; }
    
}

 

 

 

 

1.3 지네릭 클래스의 객체 생성과 사용

 

1) 참조변수와 생성자에 대입된 타입이 불일치 시 에러 발생 

Box<Grape> grapeBox = new Box<Apple>; // 에러 발생. 대입된 타입 다름

 

2) 두 타입이 상속 관계에 있어도 에러 발생

Box<Fruit> appleBox = new Box<Apple>(); // 에러 발생. 대입된 타입 다름

 

3) 두 지네릭 클래스의 타입이 상속관계이고, 대입된 타입이 같으면 가능

Box<Apple> appleBox = new FruitBox<Apple>(); // OK. 다형성

 

* 예시 코드

package Generic;

import java.util.ArrayList;

class Fruit {
    public String toString() { return "Fruit"; }
}

class Apple extends Fruit { // Fruit 상속
    public String toString() { return "Apple"; }
}

class Grape extends Fruit { // Fruit 상속
    public String toString() { return "Graple"; }
}

class Toy {
    public String toString() { return "Toy"; }
}

public class FruitBoxEx1 {
    public static void main(String[] args){

        Box<Fruit> fruitBox = new Box<Fruit>();
        Box<Apple> appleBox = new Box<Apple>();
        Box<Toy>   toyBox   = new Box<Toy>();
        //	Box<Grape> grapeBox = new Box<Apple>(); // 에러. 타입 불일치!

        fruitBox.add(new Fruit());
        fruitBox.add(new Apple()); // OK. void add(Fruit item) Apple은 Fruit의 자식

        appleBox.add(new Apple());
        appleBox.add(new Apple());
        //  appleBox.add(new Fruit()); // 에러. only Apple 만
        //  appleBox.add(new Toy()); // 에러. Box<Apple>에는 Apple만 담을 수 있음

        toyBox.add(new Toy());
        // toyBox.add(new Apple()); // 에러, Box<Toy>에는 Apple을 담을 수 없음.

        System.out.println(fruitBox);
        System.out.println(appleBox);
        System.out.println(toyBox);

    }
}

class Box<T> {

    ArrayList<T> list = new ArrayList<T>();
    void add(T item)         { list.add(item); }
    T get(int i)             { return list.get(i); }
    int size()               { return list.size(); }
    public String toString() { return list.toString(); }

}
실행결과
[Fruit, Apple]
[Apple, Apple]
[Toy]

 

 

1.4 제한된 지네릭 클래스

타입 매개변수 T에 지정할 수 있는 타입의 종류를 제한하는 방법

class FruitBox<T extends Fruit> { // Fruit의 자손만 타입으로 지정 가능!!
	ArrayList<T> list = new ArrayList<T>();
}

class sample{

	public static void main(String[] args){
    
    	FruitBox<Fruit> fruitBox = new FruitBox<Fruit>();
        fruitBox.add(new Apple()); // OK. Apple은 Fruit의 자손
        fruitBox.add(new Graple()); // OK. Grape는 Fruitdml 자손
    }
	
}

 

인터페이스를 구현해야한다면 이 때도 "extends"를 사용. Not "implemnet"

Fruit의 자손이면서, Eatable 인터페이스도 구현할 경우 " & " 기호 사용

interface Eatable {}
class FruitBox<T extends Fruit & Eatable> { ... }

 

 

1.5 와일드 카드

지네릭 클래스에서 static 메서드에는 타입 매개변수 T를 매개변수에 사용할 수 없음.

static 메서드의 경우 특정 타입을 지정해줘야하는데. 

아래와 같이 FruitBox<Apple> 타입 객체는 makeJuice() 매개변수가 될 수 없으므로, 여러 가지 타입의 매개변수를 갖는 makeJuice()를 만들어야함.

static Juice makeJuice(FruitBox<Fruit> box) {
	String tmp = "";
    for(Fruit f : box.getList()) tmp += f + " ";
    return new Juice(tmp);
}

static Juci makeJuice(FruitBox<Apple> box) {
	String tmp = "";
    for(Fruit f : box.getList()) tmp += f + " ";
    return new Juice(tmp);
}

하지만, 위와 같이 오버로딩하면 컴파일 에러가 발생!  ➣ 지네릭 타입이 다른 타입 만으로는 오버로딩 성립 안함

 

이를 해결하기 위한(static 메서드에서 여러 지네릭 타입 사용) 기능이 바로 와일드 카드!!

<? extends T>  와일드카드의 상한제한. T와 그 자손들만 가능
<? super T>      와일드카드의 하한제한. T와 그 조상들만 가능
<?>                    모든 타입 가능. <? extends Object>와 동일

 

*<? extends T> 예시

static Juice makeJuice(FruitBox<? extends Fruit> box) {
	String tmp = "";
    for(Fruit f : box.getList()) tmp += f + " ";
    return new Juice(tmp);
}

 

 

* <? super T> 예시

static <T> void sort(List<T> list, Comparator<? super Apple> c)

 

comparator<? super Apple> 의미 : comparator 타입 매개변수로 Apple과 그 조상이 가능

Comparator<? super Apple> : Comparator<Apple>, Comparator<Fruit>, Comparator<Object> 가능
Comparator<? super Grape> : Comparator<Grape>, Comparator<Fruit>, Comparator<Object> 가능

'Java' 카테고리의 다른 글

[Stream API] 스트림 만들기  (0) 2023.10.28
[Stream API] 스트림이란?  (0) 2023.10.28
[Java] 자바 언어의 특징  (0) 2023.07.24
[Java] BufferedReader / BufferedWriter 정리  (0) 2023.02.22
[Java] 접근제어자 혹은 접근제한자  (0) 2022.11.20
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/07   »
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
글 보관함