λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°
μ–Έμ–΄/Java

[Java] 13. μ œλ„€λ¦­ (2) - 3~5

by μ Όμ Ό39 2025. 8. 4.

13.3 μ œλ„€λ¦­ λ©”μ†Œλ“œ

* μ œλ„€λ¦­ λ©”μ†Œλ“œ : νƒ€μž… νŒŒλΌλ―Έν„°λ₯Ό κ°€μ§€κ³  μžˆλŠ” λ©”μ†Œλ“œ
    - νƒ€μž… νŒŒλΌλ―Έν„°κ°€ λ©”μ†Œλ“œ 선언뢀에 μ •μ˜λ¨ (<- μ œλ„€λ¦­ νƒ€μž…κ³Ό 차이!)
    - 객체 νƒ€μž…μ— μƒκ΄€μ—†λŠ” μœ μ—°ν•œ λ©”μ†Œλ“œ μ •μ˜ κ°€λŠ₯

    - 리턴 νƒ€μž… μ•žμ— <> 기호λ₯Ό μΆ”κ°€ν•˜κ³  νƒ€μž… νŒŒλΌλ―Έν„°λ₯Ό μ •μ˜ν•¨

    - 리턴 νƒ€μž…, λ§€κ°œλ³€μˆ˜ νƒ€μž…μ—μ„œ μ‚¬μš©ν•¨

ꡬ뢄 μ œλ„€λ¦­ νƒ€μž… μ œλ„€λ¦­ λ©”μ†Œλ“œ
μ •μ˜ μœ„μΉ˜ 클래슀, μΈν„°νŽ˜μ΄μŠ€ 선언뢀에 <T> λ“±μœΌλ‘œ λͺ…μ‹œ λ©”μ†Œλ“œ 선언뢀에 <T> λ“±μœΌλ‘œ λͺ…μ‹œ
적용 λ²”μœ„ ν•΄λ‹Ή 클래슀/μΈν„°νŽ˜μ΄μŠ€ λ‚΄μ—μ„œ 멀버 λ³€μˆ˜, λ©”μ†Œλ“œ λ“± 전체 ν•΄λ‹Ή λ©”μ†Œλ“œ λ‚΄λΆ€μ—μ„œλ§Œ μ‚¬μš©, λ©”μ†Œλ“œ μ‹œκ·Έλ‹ˆμ²˜μ—λ§Œ 영ν–₯
μ˜ˆμ‹œ class Box<T> { T value; } <T> void print(T item) {...}
μž¬μ‚¬μš©μ„± ν•˜λ‚˜μ˜ νƒ€μž…μ— μ—¬λŸ¬ 데이터 νƒ€μž…μ„ μ μš©ν•  수 있음 ν•˜λ‚˜μ˜ λ©”μ†Œλ“œμ—μ„œ μ—¬λŸ¬ 데이터 νƒ€μž… 처리 κ°€λŠ₯
μΈμŠ€ν„΄μŠ€ 생성 Box<Integer> intBox = new Box<>(); print(123); print("hello"); λ“± λ‹€μ–‘ν•œ νƒ€μž… μ‚¬μš© κ°€λŠ₯
μ œλ„€λ¦­ νƒ€μž… μ‚¬μš© μ°Έμ‘° λ³€μˆ˜μ—μ„œ νƒ€μž… μ§€μ •, νƒ€μž… 일관성 확보 λ§€ ν˜ΈμΆœλ§ˆλ‹€ νƒ€μž… μΆ”λ‘ , μœ μ—°ν•˜κ²Œ μ‚¬μš© κ°€λŠ₯
 
  • μ œλ„€λ¦­ νƒ€μž…
    • ν΄λž˜μŠ€λ‚˜ μΈν„°νŽ˜μ΄μŠ€ 전체에 걸쳐 νƒ€μž… νŒŒλΌλ―Έν„°λ₯Ό μ‚¬μš©ν•¨
    • μ˜ˆμ‹œ: Box<T>λŠ” BoxλΌλŠ” ν΄λž˜μŠ€μ—μ„œ TλΌλŠ” νƒ€μž…μ„ 내뢀에 걸쳐 계속 μ‚¬μš©ν•¨
    • 주둜 자료ꡬ쑰 클래슀(ArrayList, HashMap λ“±)μ—μ„œ 많이 μ‚¬μš©λ˜λ©°, 생성할 λ•Œ νƒ€μž…μ„ ν•œλ²ˆ μ •ν•˜λ©΄ ν•΄λ‹Ή μΈμŠ€ν„΄μŠ€μ—μ„œ λ‚΄λ‚΄ κ·Έ νƒ€μž…λ§Œ μ‚¬μš©ν•¨
  • μ œλ„€λ¦­ λ©”μ†Œλ“œ
    • λ©”μ†Œλ“œ μ„ μ–Έμ—λ§Œ νƒ€μž… νŒŒλΌλ―Έν„°λ₯Ό μ‚¬μš©ν•¨(λ©”μ†Œλ“œ μ‹œκ·Έλ‹ˆμ²˜ μ™Όμͺ½μ— <T> λ“± λͺ…μ‹œ).
    • 클래슀의 μ œλ„€λ¦­ νƒ€μž…κ³Ό λ³„κ°œλ‘œ, λ©”μ†Œλ“œλ§ˆλ‹€ λ…λ¦½μ μœΌλ‘œ λ‹€μ–‘ν•œ νƒ€μž…μ„ 처리 κ°€λŠ₯
    • μ˜ˆμ‹œμ²˜λŸΌ print(T item)은 ν˜ΈμΆœν•  λ•Œλ§ˆλ‹€ int, String λ“± μ–΄λ–€ νƒ€μž…μ΄λ“  μ“Έ 수 있음
// μ œλ„€λ¦­ νƒ€μž… μ˜ˆμ‹œ
class Box<T> {
    private T value;
    public void set(T value) { this.value = value; }
    public T get() { return value; }
}

// μ œλ„€λ¦­ λ©”μ†Œλ“œ μ˜ˆμ‹œ
class Util {
    public static <T> void print(T item) { System.out.println(item); }
}
 
  • Box<Integer>, Box<String>처럼 각각 νƒ€μž…λ³„ μΈμŠ€ν„΄μŠ€ 생성.
  • Util.print(100); Util.print("hi");처럼 λ‹€μ–‘ν•œ νƒ€μž…μ˜ 값을 λ©”μ†Œλ“œλ‘œ 좜λ ₯ κ°€λŠ₯.
  • 핡심
    • "νƒ€μž… 전체에 적용" → μ œλ„€λ¦­ νƒ€μž…
    • "λ©”μ†Œλ“œ μ•ˆμ—μ„œλ§Œ 적용" → μ œλ„€λ¦­ λ©”μ†Œλ“œ

 

public <A, B, ...> λ¦¬ν„΄νƒ€μž… λ©”μ†Œλ“œλͺ…(λ§€κ°œλ³€μˆ˜, ...) { ... }
      //νƒ€μž… νŒŒλΌλ―Έν„° μ •μ˜

 

    (예) boxing() λ©”μ†Œλ“œμ—μ„œ νƒ€μž… νŒŒλΌλ―Έν„°λ‘œ <T>λ₯Ό μ •μ˜ν•˜κ³ , λ§€κ°œλ³€μˆ˜ νƒ€μž…κ³Ό 리턴 νƒ€μž…μ—μ„œ Tλ₯Ό μ‚¬μš©
            (리턴 νƒ€μž… : Tλ₯Ό λ‚΄μš©λ¬Όλ‘œ κ°–λŠ” Box 객체)

public <T> Box<T> boxing(T t) { ... }
  • public : μ ‘κ·Ό μ œμ–΄μž
  • <T> : μ œλ„€λ¦­ νƒ€μž… νŒŒλΌλ―Έν„°(νƒ€μž… λ³€μˆ˜)μ„ μ–Έ   <-- 이 뢀뢄이 있으면 이 λ©”μ†Œλ“œλŠ” μ œλ„€λ¦­ λ©”μ†Œλ“œμž„
    • 이 λ©”μ†Œλ“œκ°€ TλΌλŠ” μž„μ˜μ˜ νƒ€μž…μ„ μ‚¬μš©ν•  수 μžˆλ‹€λŠ” 뜻
    • λ©”μ†Œλ“œμ˜ λ°˜ν™˜ νƒ€μž… λ°”λ‘œ μ•žμ— μœ„μΉ˜ν•΄μ•Ό 함
  • Box<T> : 리턴 νƒ€μž…
    • 이 λ©”μ†Œλ“œλŠ” Box<T> 객체λ₯Ό λ°˜ν™˜ν•œλ‹€λŠ” 뜻
    • Tκ°€ μ–΄λ–€ νƒ€μž…μ΄ λ˜λŠ”μ§€λŠ” λ©”μ†Œλ“œ 호좜 μ‹œμ— 결정됨
  • boxing : λ©”μ†Œλ“œ 이름
  • (T t) : νŒŒλΌλ―Έν„°(λ§€κ°œλ³€μˆ˜) μ„ μ–Έ
    • 이 λ©”μ†Œλ“œλŠ” ν•˜λ‚˜μ˜ νŒŒλΌλ―Έν„° tλ₯Ό λ°›λŠ”λ°, 이 t의 νƒ€μž… μ—­μ‹œ Tμž„
    • boxing(100) 이면 TλŠ” Integer둜 μžλ™ 좔둠됨
public <T> Box<T> boxing(T t) {
    Box<T> box = new Box<>();
    box.set(t);
    return box;
}
 

    - νŒŒλΌλ―Έν„°λ‘œ 받은 tλ₯Ό λ‹΄κ³  μžˆλŠ” Box<T> 객체λ₯Ό λ§Œλ“€μ–΄ λ°˜ν™˜ν•¨

Box<Integer> intBox = boxing(123);
Box<String> strBox = boxing("hello");
 
     - ν˜ΈμΆœν•  λ•Œ λ„˜κΈ°λŠ” 값에 따라 Tκ°€ λ°”λ€Œκ³ , 그에 맞좰 λ¦¬ν„΄κ°’μ˜ νƒ€μž…μ΄ λ°”λ€œ
 
  • <T> : "이 ν•¨μˆ˜ μ•ˆμ—μ„  μž„μ˜λ‘œ TλΌλŠ” νƒ€μž…μ„ μ“Έ 수 μžˆλ‹€"λŠ” μ„ μ–Έ
  • Box<T> boxing(T t) : "T νƒ€μž… 값을 λ°›μ•„μ„œ, κ·Έ νƒ€μž…μ„ λ‹΄λŠ” Box 객체λ₯Ό λ°˜ν™˜ν•œλ‹€"
  • t의 νƒ€μž… μΆ”λ‘ : λ©”μ†Œλ“œ 호좜 μ‹œ, μ „λ‹¬λœ μΈμˆ˜λ‘œλΆ€ν„° → 컴파일 νƒ€μž„μ— 결정됨
  • μ½”λ“œ μ‹€ν–‰(λŸ°νƒ€μž„) μ€‘μ—λŠ” νƒ€μž…에 κ΄€λ ¨ν•΄ μΆ”κ°€ 좔둠이 μΌμ–΄λ‚˜μ§€ μ•Šκ³ , 이미 μ»΄νŒŒμΌλŸ¬κ°€ ν™•μ •ν•œ νƒ€μž…μœΌλ‘œ μ²˜λ¦¬ν•¨

    -> ν˜ΈμΆœν•  λ•Œ μ–΄λ–€ 값을 λ„˜κΈ°λŠλƒμ— 따라 μ»΄νŒŒμΌκ³Όμ •μ—μ„œ μžλ™μœΌλ‘œ νƒ€μž…μ΄ μ •ν•΄μ Έμ„œ νƒ€μž… μ•ˆμ „μ„±μ„ ν™•λ³΄ν•˜κ²Œ 됨

 

    (예 2) μ œλ„€λ¦­ λ©”μ†Œλ“œ μ„ μ–Έ, 호좜

public class Box<T> {
    //ν•„λ“œ
    private T t;
    
    //getter λ©”μ†Œλ“œ
    public T get() {
        return t;
    }
    
    //setter λ©”μ†Œλ“œ
    public void set(T t) {
        this.t = t;
    }
}
public class GenericExample {
    //μ œλ„€λ¦­ λ©”μ†Œλ“œ
    public static <T> Box<T> boxing (T t){ //νƒ€μž… νŒŒλΌλ―Έν„° T μ •μ˜
        Box<T> box = new Box<T>();
        box.set(t);
        return box;
    }
    
    public static void main(String[] args) {
        //μ œλ„€λ¦­ λ©”μ†Œλ“œ 호좜
        Box<Integer> box1 = boxing(100); //Tλ₯Ό Integer둜 λŒ€μ²΄
        int intValue = box1.get();
        System.out.println(intValue);
        
        //μ œλ„€λ¦­ λ©”μ†Œλ“œ 호좜
        Box<String> box2 = boxing("홍길동"); //Tλ₯Ό String으둜 λŒ€μ²΄
        String strValue = box2.get();
        System.out.println(strValue);
    }
}

 

    - λ‹€λ₯Έ μ˜ˆμ‹œλ“€
    (예 3) swap - λ°°μ—΄μ˜ 두 값을 νƒ€μž…μ— 상관없이 비ꡐ κ°€λŠ₯

public static <T> void swap(T[] arr, int i, int j) {
    T temp = arr[i];
    arr[i] = arr[j];
    arr[j] = temp;
}

     (예 4) 비ꡐ - μ—¬λŸ¬ νƒ€μž…(T, V)의 값을 κ°–λŠ” People κ°μ²΄λΌλ¦¬ 값이 같은지 비ꡐ할 λ•Œ, νƒ€μž…μ— 상관없이 μ‚¬μš©

public static <T, V> boolean compare(People<T, V> p1, People<T, V> p2) {
    boolean nameCompare = p1.getName().equals(p2.getName());
    boolean ageCompare = p1.getAge().equals(p2.getAge());
    return nameCompare && ageCompare;
}

 

13.4 μ œν•œλœ νƒ€μž… νŒŒλΌλ―Έν„°

* μ œν•œλœ νƒ€μž… νŒŒλΌλ―Έν„° (bounded type parameter)
    - νƒ€μž… νŒŒλΌλ―Έν„°λ₯Ό λŒ€μ²΄ν•˜λŠ” ꡬ체적인 νƒ€μž…μ„ μ œν•œν•  ν•„μš”κ°€ μžˆλŠ” κ²½μš°κ°€ 쑴재
        (예: 숫자λ₯Ό μ—°μ‚°ν•˜λŠ” μ œλ„€λ¦­ λ©”μ†Œλ“œλŠ” λŒ€μ²΄ νƒ€μž…μœΌλ‘œ Number λ˜λŠ” μžμ‹ 클래슀둜 μ œν•œ)

public <T extends μƒμœ„νƒ€μž…> λ¦¬ν„΄νƒ€μž… λ©”μ†Œλ“œ(λ§€κ°œλ³€μˆ˜, ... ) { ... }

    - μœ„μ˜ μƒμœ„νƒ€μž…μ€ 클래슀 뿐만 μ•„λ‹ˆλΌ μΈν„°νŽ˜μ΄μŠ€λ„ κ°€λŠ₯ (<- μΈν„°νŽ˜μ΄μŠ€λΌκ³  ν•΄μ„œ implementsλ₯Ό μ‚¬μš©ν•˜μ§€ μ•ŠμŒ)

    (예 1) Number νƒ€μž…κ³Ό μžμ‹ ν΄λž˜μŠ€μ—λ§Œ λŒ€μ²΄ κ°€λŠ₯ν•œ νƒ€μž… νŒŒλΌλ―Έν„°λ₯Ό μ •μ˜ν•˜κΈ°

public <T extends Number> boolean compare(T t1, T t2) {
    double v1 = t1.doubleValue();  //Number의 doubleValue() λ©”μ†Œλ“œ μ‚¬μš©
    double v2 = t2.doubleValue();  //Number의 doubleValue() λ©”μ†Œλ“œ μ‚¬μš©
    return (v1 == v2);
}
public static void main(String[] args) {
    //μ œλ„€λ¦­ λ©”μ†Œλ“œ 호좜
    boolean result1 = compare(10, 20);  //<- Tλ₯Ό Integer νƒ€μž…μœΌλ‘œ λŒ€μ²΄
    System.out.println(result1);
}

    - νƒ€μž… νŒŒλΌλ―Έν„°κ°€ Number νƒ€μž…μœΌλ‘œ μ œν•œλ˜λ©΄μ„œ Object의 λ©”μ†Œλ“œλΏλ§Œ μ•„λ‹ˆλΌ Numberκ°€ κ°€μ§€κ³  μžˆλŠ” λ©”μ†Œλ“œλ„ μ‚¬μš© κ°€λŠ₯

 

13.5 μ™€μΌλ“œμΉ΄λ“œ νƒ€μž… νŒŒλΌλ―Έν„°

    - μ œλ„€λ¦­ νƒ€μž…μ„ λ§€κ°œκ°’μ΄λ‚˜ 리턴 νƒ€μž…μœΌλ‘œ μ‚¬μš©ν•  λ•Œ, νƒ€μž… νŒŒλΌλ―Έν„°λ‘œ ? (μ™€μΌλ“œμΉ΄λ“œ)λ₯Ό μ‚¬μš© κ°€λŠ₯
    * ? μ™€μΌλ“œμΉ΄λ“œ : λ²”μœ„μ— μžˆλŠ” λͺ¨λ“  νƒ€μž…μœΌλ‘œ λŒ€μ²΄ κ°€λŠ₯ν•˜λ‹€λŠ” ν‘œμ‹œ

    1) νƒ€μž… νŒŒλΌλ―Έν„°μ˜ λŒ€μ²΄ νƒ€μž…μœΌλ‘œ Student와 μžμ‹ 클래슀만 κ°€λŠ₯ν•˜λ„λ‘ λ§€κ°œλ³€μˆ˜ μ„ μ–Έν•˜κΈ° => <? extends Student>

λ¦¬ν„΄νƒ€μž… λ©”μ†Œλ“œλͺ…(μ œλ„€λ¦­νƒ€μž…<? extends Studnet> λ³€μˆ˜) { ... }

    2) νƒ€μž… νŒŒλΌλ―Έν„°μ˜ λŒ€μ²΄ νƒ€μž…μœΌλ‘œ Workerκ³Ό λΆ€λͺ¨ 클래슀만 κ°€λŠ₯ν•˜λ„λ‘ λ§€κ°œλ³€μˆ˜ μ„ μ–Έν•˜κΈ° => <? super Worker>

λ¦¬ν„΄νƒ€μž… λ©”μ†Œλ“œλͺ…(μ œλ„€λ¦­νƒ€μž…<? super Worker> λ³€μˆ˜) { ... }

    3) νƒ€μž… νŒŒλΌλ―Έν„°μ˜ λŒ€μ²΄ νƒ€μž…μœΌλ‘œ μ–΄λ–€ νƒ€μž…μ΄λ“  κ°€λŠ₯ν•˜λ„λ‘ λ§€κ°œλ³€μˆ˜ μ„ μ–Έν•˜κΈ° => <?>

λ¦¬ν„΄νƒ€μž… λ©”μ†Œλ“œλͺ…(μ œλ„€λ¦­νƒ€μž…<?> λ³€μˆ˜) { ... }

 

    (예 1) λͺ¨λ“  μ‚¬λžŒ / 학생 / 일반인 에 따라 μ œν•œν•˜κΈ°

public class Applicant<T> {
    public T kind;
    
    public Applicant(T kind) {
        this.kind = kind;
    }
}
public class Course {
    //λͺ¨λ“  μ‚¬λžŒμ΄ 등둝 κ°€λŠ₯
    public static void registerCourse1(Applicant<?> applicant) {
        System.out.println(applicant.kind.getClass().getSimpleName() + "1 등둝");
    }
    
    //ν•™μƒλ§Œ 등둝 κ°€λŠ₯
    public static void registerCourse2(Applicant<? extends Student> applicant) {
        System.out.println(applicant.kind.getClass().getSimpleName() + "2 등둝");
    }
    
    //일반인만 등둝 κ°€λŠ₯
    public static void registerCourse3(Applicant<? super Worker> applicant) {
        System.out.println(applicant.kind.getClass().getSimpleName() + "3 등둝");
    }
}
public class GenericExample {
    public static void main(String[] args) {
        //λͺ¨λ“  μ‚¬λžŒμ΄ μ‹ μ²­ κ°€λŠ₯
        Course.registerCourse1(new Applicant<Person>(new Person()));
        
        //ν•™μƒλ§Œ μ‹ μ²­ κ°€λŠ₯
        Course.registerCourse2(new Applicant<Student>(new Student()));
        
        //일반인만 μ‹ μ²­ κ°€λŠ₯
        Course.registerCourse3(new Applicant<Worker>(new Worker()));
    }
}