Coding Planet

μ§€λ„€λ¦­μŠ€(Generics) 핡심 μ •λ¦¬ν•˜κΈ° - 지넀릭 클래슀 λ³Έλ¬Έ

πŸ’» Java Study/Java 이둠 정리

μ§€λ„€λ¦­μŠ€(Generics) 핡심 μ •λ¦¬ν•˜κΈ° - 지넀릭 클래슀

jhj.sharon 2024. 3. 5. 16:46
λ°˜μ‘ν˜•

 

μžλ°” μž…λ¬Έ λ ˆλ²¨μ—μ„œ 개발자 λ¬Έμ„œλ₯Ό λ³΄κ±°λ‚˜ github의 λ‹€λ₯Έ μ½”λ“œλ“€μ„ 보닀 보면 Box<T>와 같은 λ‚―μ„  ν˜•νƒœμ˜ λ¬Έμž₯을 λ§Œλ‚˜κ²Œ λœλ‹€. μ΄λŸ¬ν•œ 것듀이 μ§€λ„€λ¦­μŠ€μΈλ° 남ꢁ성 μ €μžμ˜ μžλ°”μ˜ 정석을 μ°Έκ³ ν•˜μ—¬ μž…λ¬Έμˆ˜μ€€μ—μ„œ κ°„λ‹¨ν•˜κ²Œ μ‚΄νŽ΄λ³΄κ³ μž ν•œλ‹€.
μ–΄λ ΅λ‹€λ©΄ κ°•μ‘°λœ λ‚΄μš©λ§Œ 봐도 λŒ€μΆ© 감을 μž‘μ„ 수 μžˆλ‹€.

 

 

| μ§€λ„€λ¦­μŠ€λž€?

μ§€λ„€λ¦­μŠ€λŠ” λ‹€μ–‘ν•œ νƒ€μž…μ˜ 객체듀을 λ‹€λ£¨λŠ” λ©”μ„œλ“œλ‚˜ μ»¬λ ‰μ…˜ ν΄λž˜μŠ€μ— 컴파일 μ‹œ 의 νƒ€μž…μ²΄ν¬(compile-time type check)λ₯Ό ν•΄μ£ΌλŠ” κΈ°λŠ₯이닀. 객체의 νƒ€μž…μ„ 컴파일 μ‹œμ— μ²΄ν¬ν•˜κΈ° λ•Œλ¬Έμ— 객체의 νƒ€μž… μ•ˆμ „μ„±μ„ 높이고 ν˜•λ³€ν™˜μ˜ λ²ˆκ±°λ‘œμ›€μ΄ 쀄어든닀.

-μžλ°”μ˜ 정석

 

1. 컴파일 μ‹œ 의 νƒ€μž…μ²΄ν¬(compile-time type check)

μ§€λ„€λ¦­μŠ€λ₯Ό μ‚¬μš©ν•˜λ©΄, μ»¬λ ‰μ…˜μ— μ €μž₯λ˜λŠ” 객체의 νƒ€μž…μ„ 컴파일 μ‹œμ μ— μ²΄ν¬ν•œλ‹€. μ΄λŠ” 잘λͺ»λœ νƒ€μž…μ˜ 객체가 μΆ”κ°€λ˜λŠ” 것을 λ°©μ§€ν•˜μ—¬ μ‹€ν–‰μ‹œκ°„(runtime)의 'ClassCastException'을 μ˜ˆλ°©ν•œλ‹€.

 μ˜ˆλ₯Ό λ“€μ–΄, List<String>은 λ¬Έμžμ—΄λ§Œμ„ μš”μ†Œλ‘œ κ°€μ§ˆ 수 있으며, μ»΄νŒŒμΌλŸ¬λŠ” 이 λ¦¬μŠ€νŠΈμ— λ¬Έμžμ—΄μ΄ μ•„λ‹Œ λ‹€λ₯Έ νƒ€μž…μ˜ 객체가 μΆ”κ°€λ˜λ €κ³  ν•  λ•Œ 컴파일 였λ₯˜λ₯Ό λ°œμƒμ‹œν‚¨λ‹€.

 

2. 객체 νƒ€μž…μ˜ μ•ˆμ „μ„±(Type Safety)

μ§€λ„€λ¦­μŠ€λ₯Ό μ‚¬μš©ν•¨μœΌλ‘œμ¨, μ»¬λ ‰μ…˜μ—μ„œμ˜ 객체λ₯Ό μ½μ–΄μ˜¬ λ•Œ λͺ…μ‹œμ μΈ ν˜•λ³€ν™˜μ΄ ν•„μš” μ—†κ²Œ λœλ‹€. μ»΄νŒŒμΌλŸ¬κ°€ 이미 μš”μ†Œμ˜ νƒ€μž…μ„ μ•Œκ³  있기 λ•Œλ¬Έμ—, μ˜¬λ°”λ₯Έ νƒ€μž…μ˜ 객체만이 처리될 κ²ƒμž„μ„ 보μž₯ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. μ΄λŠ” ν”„λ‘œκ·Ήλž¨μ˜ μ•ˆμ „μ„±μ„ μ˜¬λ €μ€€λ‹€.

 

3. ν˜•λ³€ν™˜μ˜ λ²ˆκ±°λ‘œμ›€ κ°μ†Œ

μ§€λ„€λ¦­μŠ€λ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠλŠ” 경우, μ»¬λ ‰μ…˜μ—μ„œ 객체λ₯Ό κΊΌλ‚Ό λ•Œλ§ˆλ‹€ ν•΄λ‹Ή 객체λ₯Ό μ μ ˆν•œ νƒ€μž…μœΌλ‘œ ν˜•λ³€ν™˜ν•΄μ•Όν•œλ‹€. μ΄λŠ” μ½”λ“œλ₯Ό 번거둭게 λ§Œλ“€ 뿐만 μ•„λ‹ˆλΌ, 잘λͺ»λœ νƒ€μž…μœΌλ‘œμ˜ ν˜•λ³€ν™˜ κ°€λŠ₯성을 λ‚΄ν¬ν•˜κ³  μžˆμ–΄ 였λ₯˜μ˜ κ°€λŠ₯성을 높인닀. μ§€λ„€λ¦­μŠ€λ₯Ό μ‚¬μš©ν•˜λ©΄ μ΄λŸ¬ν•œ ν˜•λ³€ν™˜μ΄ λΆˆν•„μš”ν•΄μ§€λ―€λ‘œ, μ½”λ“œκ°€ 더 κ°„κ²°ν•˜κ³  μ•ˆμ „ν•΄μ§„λ‹€.

 

// μ§€λ„€λ¦­μŠ€ 없이 μ»¬λ ‰μ…˜μ„ μ‚¬μš©ν•  λ•Œ
List list = new ArrayList();
list.add("hello");
String str = (String) list.get(0); // λͺ…μ‹œμ  ν˜•λ³€ν™˜ ν•„μš”

//μ§€λ„€λ¦­μŠ€λ₯Ό μ‚¬μš©ν•  λ•Œ
List<String> list = new ArrayList<>();
list.add("hello");
String str = list.get(0); // ν˜•λ³€ν™˜ λΆˆν•„μš”

 

 

 

β–Ά κ°„λ‹¨νžˆ λ§ν•˜μžλ©΄ λ‹€λ£° 객체의 νƒ€μž…μ„ 미리 λͺ…μ‹œν•΄μ€ŒμœΌλ‘œμ¨ 번거둜운 ν˜•λ³€ν™˜μ„ μ€„μ—¬μ€€λ‹€λŠ” μ˜λ―Έμ΄λ‹€.

 

 

 

| 지넀릭 클래슀 μ„ μ–Έν•˜κ³  μ‚¬μš©ν•˜κΈ°

지넀릭 νƒ€μž…μ€ ν΄λž˜μŠ€μ™€ λ©”μ„œλ“œμ— μ„ μ–Έν•  수 μžˆλ‹€. 이번 ν¬μŠ€νŠΈμ—μ„œλŠ” 클래슀 μœ„μ£Όλ‘œ μ•Œμ•„λ³΄λ €κ³  ν•œλ‹€.

μ§€λ„€λ¦­μŠ€λ₯Ό μ‚¬μš©ν•˜μ—¬ Box 클래슀λ₯Ό λ³€κ²½ν•˜λŠ” μ˜ˆμ΄λ‹€. Box ν΄λž˜μŠ€κ°€ λ‹€μ–‘ν•œ νƒ€μž…μ˜ 객체λ₯Ό μ €μž₯ν•  수 μžˆλ„λ‘ 클래슀 μ˜†μ— <T>λ₯Ό λΆ™μ΄λ©΄λœλ‹€. <T>λŠ” νŒŒλΌλ―Έν„° νƒ€μž…μ„ λ‚˜νƒ€λ‚΄λ©° Box 클래슀λ₯Ό μ‚¬μš©ν•  λ•Œ ꡬ체적인 νƒ€μž…μœΌλ‘œ λŒ€μ²΄λœλ‹€. (ex. Box<String>, Box<Integer>). μ΄λ ‡κ²Œ ν•˜λ©΄ Box ν΄λž˜μŠ€λŠ” λ‹€μ–‘ν•œ νƒ€μž…μ˜ 객체λ₯Ό μ €μž₯ν•  수 있게 λœλ‹€. 

  • <T> : νƒ€μž…λ³€μˆ˜(type variable). μž„μ˜μ˜ μ°Έμ‘°ν˜• νƒ€μž…μ„ μ˜λ―Έν•œλ‹€. T말고도 λ‹€λ₯Έ 것을 μ‚¬μš©ν•΄λ„ λœλ‹€(ex. ArrayList<E>, Map<K,V>)
//일반 클래슀
public class Box {
    Object item;
    void setItem(Object item){ this.item = item;}
     Object getItem(){return item;}
}

//μ§€λ„€λ¦­μŠ€ 클래슀둜 λ³€ν™˜
public class Box<T> {    
    private T item;    
    public void setItem(T item){this.item = item; }
        public T getItem(){ return item;}
}

 

지넀릭 클래슀λ₯Ό μ‚¬μš©ν•  λ•ŒλŠ” λ‹€μŒκ³Ό 같이 μ°Έμ‘°λ³€μˆ˜μ™€ μƒμ„±μž νƒ€μž… TλŒ€μ‹  μ‚¬μš©λ  μ‹€μ œ νƒ€μž…μ„ μ§€μ •ν•΄μ€˜μ•Ό ν•œλ‹€.

μ‹€μ œ IDEμ—μ„œ μ§€μ •λœ νƒ€μž… μ΄μ™Έμ˜ νƒ€μž…μ„ μ‚¬μš©ν•  경우 μ—λŸ¬κ°€ λ°œμƒν•œλ‹€.

public class Box<T> {

    private T item;

    public void setItem(T item){
        this.item = item;
    }

    public T getItem(){
        return item;
    }

    public static void main(String[] args) {
        Box<String> b = new Box<String>();
        b.setItem(new Object()); // μ—λŸ¬. String νƒ€μž… μ™Έ μ§€μ •λΆˆκ°€
        b.setItem("Planet"); // O. String 지정가λŠ₯
        String item = b.getItem(); // ν˜•λ³€ν™˜ λΆˆν•„μš”
    }
}

 

 

 

 

 

 

 

| 지넀릭 μ£Όμš” μš©μ–΄ 정리

  • Box<T> : 지넀릭 클래슀. 'T의 Box' λ˜λŠ” 'T Box'라고 μ½λŠ”λ‹€.
  • T : νƒ€μž…λ³€μˆ˜ ν˜Ήμ€ νƒ€μž… λ§€κ°œλ³€μˆ˜(TλŠ” νƒ€μž… 문자).
  • Box : μ›μ‹œνƒ€μž…

 

  • λŒ€μž…λœ νƒ€μž… : 지넀릭 ν΄λž˜μŠ€μ— μ§€μ •λœ νƒ€μž….
  • 지넀릭 νƒ€μž… 호좜 : λ§€κ°œλ³€μˆ˜μ— νƒ€μž…μ„ μ§€μ •ν•˜λŠ” 것.

 

 

 

| μ§€λ„€λ¦­μŠ€ μ‚¬μš©μ˜ μ œν•œ

1. static λ©€λ²„μ—λŠ” νƒ€μž… λ³€μˆ˜ Tλ₯Ό μ„ μ–Έν•  수 μ—†λ‹€.

static λ©€λ²„λŠ” λͺ¨λ“  객체에 λŒ€ν•΄ λ™μΌν•˜κ²Œ λ™μž‘ν•΄μ•Όν•˜λ―€λ‘œ static 멀버에 νƒ€μž… λ³€μˆ˜ Tλ₯Ό μ‚¬μš©ν•  수 μ—†λ‹€. TλŠ” μΈμŠ€ν„΄μŠ€λ³€μˆ˜λ‘œ κ°„μ£Όλ˜κΈ° λ•Œλ¬Έμ΄λ‹€. static λ©€λ²„λŠ” λŒ€μž…λœ νƒ€μž…μ˜ μ’…λ₯˜μ— 관계없이 λ™μΌν•˜μ—¬μ•Ό ν•œλ‹€.

 

2. 지넀릭 νƒ€μž…μ˜ 배열을 생성할 수 μ—†λ‹€.

지넀릭 λ°°μ—΄ νƒ€μž…μ˜ μ°Έμ‘°λ³€μˆ˜λ₯Ό μ„ μ–Έν•˜λŠ” 것은 κ°€λŠ₯ν•˜μ§€λ§Œ 'new T[10]'κ³Ό 같이 배열을 μƒμ„±ν•˜λŠ” 것은 μ•ˆλœλ‹€. 

지넀릭 배열을 생성할 수 μ—†λŠ” 것은 new μ—°μ‚°μžμ΄λ‹€. 이 μ—°μ‚°μžλŠ” 컴파일 μ‹œμ μ— νƒ€μž… Tκ°€ 무엇인지 μ •ν™•ν•˜κ²Œ μ•Œμ•„μ•Όν•œλ‹€. κ·ΈλŸ¬λ‚˜ μ•„λž˜ μ½”λ“œμ—μ„œ T의 μ‹€μ œ νƒ€μž…μ„ 컴파일 μ‹œμ μ— μ•Œ 수 μ—†λ‹€.

public class GenericArray<T> {
    private T[] array; // κ°€λŠ₯
    
    public GenericArray(int size) {
         array = new T[size]; // 컴파일 μ—λŸ¬: Generic array creation
       
    }
}

 

 

 

| 지넀릭 클래슀의 객체 생성과 μ‚¬μš©

μ•„λž˜ μ½”λ“œλŠ” 지넀릭 클래슀의 객체 생성과 μ‚¬μš©μ„ λ³΄μ—¬μ£ΌλŠ” μ˜ˆμ‹œμ΄λ‹€. μ•„λž˜ μ½”λ“œμ—μ„œ 주의 깊게 봐야 ν•  λ‚΄μš©μ€ λ‹€μŒκ³Ό κ°™λ‹€

  • JDK 1.7λΆ€ν„°λŠ” 좔정이 κ°€λŠ₯ν•œ 경우 νƒ€μž…μ„ μƒλž΅ν•  수 있게 λ˜μ—ˆλ‹€. μ°Έμ‘°λ³€μˆ˜μ˜ νƒ€μž…λ§ŒμœΌλ‘œλΆ€ν„° Box의 λŒ€μž…λœ νƒ€μž…μ„ μ•Œ 수 μžˆμœΌλ―€λ‘œ new μ—°μ‚°μž λ‹€μŒμ˜ 지넀릭 νƒ€μž… ν˜ΈμΆœμ—μ„œλŠ” Tλ₯Ό μƒλž΅κ°€λŠ₯ν•˜λ‹€.
  • λ‹€ν˜•μ„±μ˜ 원칙에 따라 Javaμ—μ„œ ν•˜μœ„ 클래슀의 μΈμŠ€ν„΄μŠ€λŠ” μƒμœ„ 클래슀 νƒ€μž…μ„ μ°Έμ‘°λ³€μˆ˜λ‘œ μ°Έμ‘°ν•  수 μžˆλ‹€. λ”°λΌμ„œ Fruit을 μƒμ†ν•œ Apple은 Fruit을 μ°Έμ‘°λ³€μˆ˜λ‘œ μ‚¬μš©ν•  수 μžˆλŠ” 것이닀. ( fruitBox.add(new Apple()); )
  • κ·ΈλŸ¬λ‚˜ Fruit은 Apple의 μƒμœ„ νƒ€μž…μ΄λ―€λ‘œ ν•˜μœ„ νƒ€μž…μ„ μ°Έμ‘°λ³€μˆ˜λ‘œ μ‚¬μš©ν•˜ γ„Ήμˆ˜ μ—†λ‹€. ( appleBox.add(new Fruit());  )
package generics;

import java.util.ArrayList;

// κΈ°λ³Έ 클래슀 μ •μ˜
class Fruit { 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";}}

// μ œλ„€λ¦­ Box 클래슀 μ‚¬μš© μ˜ˆμ‹œ
public class FruitBox1 {
    public static void main(String[] args) {
        // Fruit와 κ·Έ ν•˜μœ„ νƒ€μž…μ„ μ €μž₯ν•  수 μžˆλŠ” Box 생성
        Box<Fruit> fruitBox = new Box<>();
        // Apple νƒ€μž…λ§Œ μ €μž₯ν•  수 μžˆλŠ” Box 생성
        Box<Apple> appleBox = new Box<>();
        // Toy νƒ€μž…μ„ μ €μž₯ν•  수 μžˆλŠ” Box 생성
        Box<Toy> toyBox = new Box<>();
        // Box<Grape> grapeBox = new Box<Apple>(); // 컴파일 μ—λŸ¬, μ œλ„€λ¦­ νƒ€μž… 뢈일치

        // Fruit 객체 및 Fruit의 ν•˜μœ„ νƒ€μž… 객체λ₯Ό FruitBox에 μΆ”κ°€
        fruitBox.add(new Fruit()); // Fruit μΈμŠ€ν„΄μŠ€ μΆ”κ°€
        fruitBox.add(new Apple()); // Apple μΈμŠ€ν„΄μŠ€ μΆ”κ°€, Apple은 Fruit의 ν•˜μœ„ νƒ€μž…

        // AppleBox에 Apple μΈμŠ€ν„΄μŠ€ μΆ”κ°€
        appleBox.add(new Apple());
        // appleBox.add(new Fruit()); // 컴파일 μ—λŸ¬, Fruit은 Apple의 μƒμœ„ νƒ€μž…μ΄λ―€λ‘œ μΆ”κ°€ λΆˆκ°€
        // appleBox.add(new Toy()); // 컴파일 μ—λŸ¬, ToyλŠ” Fruit 계측ꡬ쑰와 무관

        // Box λ‚΄μš© 좜λ ₯
        System.out.println(fruitBox); // Fruit와 Apple μΈμŠ€ν„΄μŠ€κ°€ ν¬ν•¨λœ 리슀트 좜λ ₯
        System.out.println(appleBox); // Apple μΈμŠ€ν„΄μŠ€λ§Œ ν¬ν•¨λœ 리슀트 좜λ ₯
    }
}

// μ œλ„€λ¦­ Box 클래슀 μ •μ˜
class Box<T> {
    ArrayList<T> list = new ArrayList<T>(); // T νƒ€μž…μ˜ 객체λ₯Ό μ €μž₯ν•  리슀트
    
    void add(T item){ list.add(item);} // T νƒ€μž…μ˜ 객체λ₯Ό λ¦¬μŠ€νŠΈμ— μΆ”κ°€
    T get(int i){ return list.get(i);} // λ¦¬μŠ€νŠΈμ—μ„œ T νƒ€μž…μ˜ 객체λ₯Ό λ°˜ν™˜
    ArrayList<T> getList() {return list;} // 리슀트 λ°˜ν™˜
    int size() { return list.size();} // 리슀트의 크기 λ°˜ν™˜
    public String toString(){ return list.toString();} // 리슀트의 λ¬Έμžμ—΄ ν‘œν˜„ λ°˜ν™˜
}

 

 

| μ œν•œλœ 지넀릭 클래슀

νƒ€μž… λ§€κ°œλ³€μˆ˜ T에 지정할 수 μžˆλŠ” νƒ€μž…μ˜ μ’…λ₯˜λ₯Ό μ œν•œν•  수 μžˆλ‹€. 예λ₯Όλ“€μ–΄ fruitBox<T>λΌλŠ” 과일 λ°•μŠ€ ν΄λž˜μŠ€μ— 였직 과일만 담도둝 ν•  수 μžˆλ‹€(μ œν•œ μ—†μ΄λŠ” μž₯λ‚œκ°λ„ 담을 수 μžˆλ‹€. fruitBox<Toy>).

 

  • λ‹€μŒ μ½”λ“œμ—μ„œ λ³Ό 수 μžˆλ“― 지넀릭 νƒ€μž…μ— 'extends'λ₯Ό μ‚¬μš©ν•˜λ©΄ νŠΉμ • νƒ€μž…μ˜ μžμ†λ“€λ§Œ λŒ€μž…ν•  수 있게 μ œν•œν•  수 μžˆλ‹€.
  • ν΄λž˜μŠ€κ°€ μ•„λ‹ˆλΌ μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄μ•Ό ν•  λ•Œλ„ implementsκ°€ μ•„λ‹Œ extendsλ₯Ό μ‚¬μš©ν•œλ‹€.
  • 클래슀λ₯Ό μƒμ†ν•˜λ©΄μ„œ λ™μ‹œμ— μΈν„°νŽ˜μ΄μŠ€λ„ κ΅¬ν˜„ν•΄μ•Όν•œλ‹€λ©΄ '&' 기호λ₯Ό μ“΄λ‹€.
    class FruitBox<T extends Fruit & Eatable> extends Box<T> {}
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 FruitBoxEx2 {
	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);      }
	T get(int i)      { return list.get(i); }
	int size()        { return list.size();  }
	public String toString() { return list.toString();}
}
λ°˜μ‘ν˜•
Comments