[μ΄νŽ™ν‹°λΈŒ μžλ°”] Item26. 둜 νƒ€μž…μ€ μ‚¬μš©ν•˜μ§€ 말라.

2023. 3. 15. 13:41ㆍJAVA/Effective JAVA

728x90

 

[μ΄νŽ™ν‹°λΈŒ μžλ°”] Item26. 둜 νƒ€μž…μ€ μ‚¬μš©ν•˜μ§€ 말라. 

 

https://hyejin.tistory.com/1124

 

[μ΄νŽ™ν‹°λΈŒ μžλ°”] Item26. 둜 νƒ€μž…μ€ μ‚¬μš©ν•˜μ§€ 말라.

[μ΄νŽ™ν‹°λΈŒ μžλ°”] Item26. 둜(Law) νƒ€μž…μ€ μ‚¬μš©ν•˜μ§€ 말라. 4번째 μ±•ν„°λ‘œ λ“€μ–΄μ˜€λ©΄μ„œ μ œλ„€λ¦­ νƒ€μž…κ³Ό κ΄€λ ¨λœ item듀을 곡뢀할 μ˜ˆμ •μ΄λ‹€. μ œλ„€λ¦­ νƒ€μž…μ€ μ˜€λž˜μ „λΆ€ν„° λ“€μ–΄μ˜¨ κΈ°λŠ₯인데 λ­”κ°€ μ‚¬μš©ν•˜κΈ° μ–΄λ €

hyejin.tistory.com

μ €λ²ˆ κΈ€μ—μ„œ μ œλ„€λ¦­ νƒ€μž…μ— λŒ€ν•œ μš©μ–΄ 정리λ₯Ό ν–ˆκ³ , κ·ΈλŸ¬λ©΄μ„œ 둜 ν‹°μž…μ„ μ‚¬μš©ν•˜λ©΄ μ•ˆμ’‹μ€? μ΄μœ μ— λŒ€ν•΄μ„œ 살짝 μ μ—ˆλŠ”λ° 이번 κΈ€μ—μ„œ μ’€ 더 μžμ„Ένžˆ μž‘μ„±ν•œλ‹€. 

 

 

둜 νƒ€μž…μ„ μ‚¬μš©ν•˜μ§€ 말고 λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ μ‚¬μš©ν•˜λΌ. 
List numbers = new ArrayList();
numbers.add(10);
numbers.add("whiteShip");

for (Object number : numbers) {
    System.out.println("number = " + (Integer)number); // error!
}

 

        List<Integer> numbers2 = new ArrayList<>();
        numbers2.add(10);
        numbers2.add("whiteShip"); // compile error λ°œμƒ

        // ν˜•λ³€ν™˜λ„ ν•„μš” μ—†μŒ
        for (Integer integer : numbers2) {
            System.out.println("integer = " + integer);
        }

둜 νƒ€μž…μ„ μ‚¬μš©ν•œ μœ„μ˜ μ˜ˆμ œμ™€ λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ μ‚¬μš©ν•œ μ•„λž˜ 예제둜 이유λ₯Ό μ•Œ 수 μžˆλ‹€. 

λ¨Όμ € λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ λŸ°νƒ€μž„μ΄ μ•„λ‹Œ 컴파일 μ‹œμ μ— 문제λ₯Ό 찾을 수 μžˆλ‹€. 

λ°‘μ—μ„œλŠ” List<Integer> 둜 numbersλ₯Ό μ„ μ–Έν–ˆκΈ° λ•Œλ¬Έμ— 여기에 whiteShipμ΄λΌλŠ” λ¬Έμžμ—΄μ„ λ„£μœΌλ €κ³  ν•˜λ‹ˆ 컴파일 μ‹œμ μ—μ„œ λ°”λ‘œ μ—λŸ¬λ₯Ό λ‚˜νƒ€λ‚΄ λŸ°νƒ€μž„κΉŒμ§€ 가지 μ•Šκ³ λ„ μ—λŸ¬λ₯Ό μˆ˜μ •ν•  수 μžˆλ‹€.

 

그리고 μ œλ„€λ¦­μ„ ν™œμš©ν•˜λ©΄ 이 정보가 주석이 μ•„λ‹Œ νƒ€μž… μ„ μ–Έ μžμ²΄μ— λ…Ήμ•„μžˆκΈ° λ•Œλ¬Έμ— ν‘œν˜„λ ₯μ—­μ‹œ μ’‹λ‹€. 

List numbers λ₯Ό 보면 ν•„λ“œλͺ…을 numbers 을 톡해 λŒ€μΆ© μ§μž‘μ€ ν•  수 있긴 ν•˜μ§€λ§Œ μ •ν™•νžˆ μ–΄λ–€ 값이 λ“€μ–΄κ°€λŠ”μ§€μ— λŒ€ν•΄μ„œλŠ” 넣어봐야 μ•ˆλ‹€.

ν•˜μ§€λ§Œ List<Integer> numbers2λ₯Ό 보면 κ·Έλƒ₯ ν•œλˆˆμ— μ•„ 이 ListλŠ” Integerλ₯Ό λ„£λŠ”κ΅¬λ‚˜! ν•˜κ³  μ•Œ 수 μžˆλ‹€. 

 

λ”°λΌμ„œ μ œλ„€λ¦­μ„ ν™œμš©ν•˜μ§„ μ•Šμ€ λ‘œνƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ μ•ˆμ •μ„±κ³Ό ν‘œν˜„λ ₯을 μžƒλŠ”λ‹€. 

 

 

근데.. κ·Έλ ‡λ‹€λ©΄ λ‘œνƒ€μž…μ΄ ꡳ이 μ™œ μžˆλŠ”κ°€? 에 λŒ€ν•΄μ„œ μ˜λ¬Έμ„ κ°€μ§ˆ 수 μžˆλ‹€. 

κ·Έλƒ₯ λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…λ§Œ μ‚¬μš©ν•˜λ©΄ λ˜λŠ”κ±° μ•„λ‹Œκ°€?

public class Box<E /*extends Number*/> { // μ œλ„€λ¦­ 클래슀 <E> : νƒ€μž… λ§€κ°œλ³€μˆ˜ extends Number : Numberλ₯Ό 상속받은 νƒ€μž…λ§Œ κ°€λŠ₯ (ν•œμ •μ μΈ νƒ€μž… λ§€κ°œλ³€μˆ˜)

    private E item;

    private void add(E e) {
        this.item = e;
    }

    private E get() {
        return this.item;
    }

    public static void main(String[] args) {
        Box<Integer> box = new Box<>(); // <Integer> : μ‹€μ œ νƒ€μž… λ§€κ°œλ³€μˆ˜
        box.add(10);
        System.out.println(box.get() * 100);
        printBox(box);

    }
    private static void printBox(Box<?> box) { // λΉ„ν•œμ •μ  μ™€μΌλ“œ μΉ΄λ“œ νƒ€μž… (μ•„λ¬΄λŸ° νƒ€μž…μ΄λ‚˜ λŒ€μ‘μ΄ λœλ‹€.)
        System.out.println("box = " + box.get());
    }
}

μ—¬κΈ°μ„œ 보면 Box<Integer>둜 λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ μ‚¬μš©ν•΄μ„œ boxλ₯Ό μ„ μ–Έν–ˆλ‹€. 

build ν›„ byte codeλ₯Ό 보면 

컴파일런느 μ»¬λ ‰μ…˜μ—μ„œ μ›μ†Œλ₯Ό κΊΌλ‚΄λŠ” λͺ¨λ“  곳에 보이지 μ•ŠλŠ” ν˜•λ³€ν™˜μ„ μΆ”κ°€ν•˜μ—¬ μ ˆλŒ€ μ‹€νŒ¨ν•˜μ§€ μ•ŠμŒμ„ 보μž₯ν•œλ‹€. 

Box의 Integerκ°€ μ•„λ‹Œ Object인걸 확인할 수 있고, κ·Έ λ‹€μŒ CHECKCASTλ₯Ό 톡해 Integer둜 ν˜•λ³€ν™˜μ„ λ”°λ‘œ ν•΄μ£ΌλŠ” 것을 μ•Œ 수 μžˆλ‹€. 

즉, μ½”λ“œ μƒμ—μ„œλ§Œ μš°λ¦¬κ°€ μ‚¬μš©ν•˜κΈ° νŽΈν•˜κ²Œ λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ μ‚¬μš©ν•˜λŠ” κ²ƒμ΄λΌλŠ” 것이닀. 

 

λ˜ν•œ ν•˜μœ„ μžλ°” λ²„μ „κ³Όμ˜ ν˜Έν™˜μ„±μ„ μœ„ν•΄μ„œλ„ λ‘œνƒ€μž…μ„ μ§€μ›ν•œλ‹€. 

μžλ°” μ œλ„€λ¦­μ„ 받아듀이기 μ „κΉŒμ§€ 10λ…„μ΄λΌλŠ” μ‹œκ°„μ΄ κ±Έλ ΈκΈ° λ•Œλ¬Έμ— μ œλ„€λ¦­μ΄ 없이 μ§  μ½”λ“œκ°€ 이미 많이 μžˆλ‹€. 

λ”°λΌμ„œ κΈ°μ‘΄ μ½”λ“œλ₯Ό λͺ¨λ‘ μˆ˜μš©ν•˜κΈ° ν•˜λ©΄μ„œ μ œλ„€λ¦­μ„ μ‚¬μš©ν•˜λŠ” μƒˆλ‘œμš΄ μ½”λ“œμ™€λ„ 맞물렀 λŒμ•„κ°€μ•Όλ§Œ ν–ˆλ‹€. 

이 λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ ν˜Έν™˜μ„±μ„ μœ„ν•΄ 둜 νƒ€μž…μ„ μ§€μ›ν•˜κ³  μ œλ‹ˆλ¦­ κ΅¬ν˜„μ—λŠ” μ†Œκ±° 방식을 μ‚¬μš©ν•˜κΈ°λ‘œ ν–ˆλ‹€. 

 

 

 

 

List와 List<Object>의 μ°¨μ΄λŠ”?

public class Raw {
    public static void main(String[] args) {
        List<String> strings = new ArrayList<>();
        unsafeAdd(strings, Integer.valueOf(42)); // 잘λͺ»λœ 값이 λ“€μ–΄κ°ˆ 수 μžˆλ‹€!
        String s = strings.get(0); // μ»΄νŒŒμΌλŸ¬κ°€ μžλ™μœΌλ‘œ ν˜•λ³€ν™˜ μ½”λ“œλ₯Ό λ„£μ–΄μ€€λ‹€.

//        unsafeAdd2(strings, Integer.valueOf(42)); // List<Object>와 List<String>은 λ‹€λ₯Έκ²ƒμ΄κΈ° λ•Œλ¬Έμ— μ• μ΄ˆμ— μ—λŸ¬κ°€ λ‚œλ‹€.
    }

    // 둜 νƒ€μž…μ€ νƒ€μž… μ•ˆμ •μ„±μ„ μžƒμ—ˆλ‹€.
    // μ™œλƒν•˜λ©΄ μ–΄λ–€ Listλ“  일단 λ°›κ³  잘λͺ»λœ 값이 λ“€μ–΄κ°ˆ ν™•λ₯ μ΄ 많기 λ•Œλ¬Έμ΄λ‹€.
    private static void unsafeAdd(List list, Object o) {
        list.add(o);
    }

    // List<Object>λŠ”
    private static void unsafeAdd2(List<Object> list, Object o) {
        list.add(o);
    }
}

List와 List<Object>λŠ” 그럼 같은 것 μ•„λ‹Œκ°€? ν•  수 μžˆλŠ”λ° 거의 λΉ„μŠ·ν•˜μ§€λ§Œ μ•ˆμ •μ„±μ˜ λΆ€λΆ„μ—μ„œ 차이가 μžˆλ‹€. 

ListλŠ” μ œλ„€λ¦­ νƒ€μž…μ—μ„œ μ™„μ „νžˆ λ°œμ„ λΊ€ 것이고, List<Object>λŠ” λͺ¨λ“  νƒ€μž…μ„ ν—ˆμš©ν•œλ‹€λŠ” μ˜μ‚¬λ₯Ό μ»΄νŒŒμΌλŸ¬μ— λͺ…ν™•νžˆ μ „λ‹¬ν•œ 것이닀. 

unsafeAdd λ©”μ„œλ“œλ₯Ό 보면 List에 strings κ°€ λ“€μ–΄κ°€κ³ , 42κ°€ λ“€μ–΄κ°€κ³ , stringsμ—μ„œ get ν•˜λŠ” λΆ€λΆ„μ—μ„œ μ—λŸ¬κ°€ λ‚˜λŠ”λ° μ• μ΄ˆμ— 값이 λ“€μ–΄κ°€λŠ” λΆ€λΆ„μ—μ„œ 값이 잘λͺ» λ“€μ–΄κ°€λŠ” λ¬Έμ œκ°€ μžˆλ‹€. 

 

 

그런데 unsafeAdd2λ₯Ό 보면 List<Object>에 stringsλ₯Ό λ„£μœΌλ €κ³  ν•˜λ©΄ μ—λŸ¬κ°€ λ‚œλ‹€. μ™œλƒν•˜λ©΄ List<Object>와 List<String>은 λ‹€λ₯΄κΈ° λ•Œλ¬Έμ΄λ‹€. 

String이 Object의 ν•˜μœ„ ν΄λž˜μŠ€λ‹ˆκΉŒ λ˜μ§€ μ•Šλ‚˜? ν•  수 μžˆμ§€λ§Œ  λ‹€λ₯΄λ‹€. 

λ§€κ°œλ³€μˆ˜λ‘œ  Listλ₯Ό λ°›λŠ” λ©”μ„œλ“œμ— List<String>은 λ„˜κΈΈ 수 μžˆμ§€λ§Œ, List<Object> λ₯Ό λ°›λŠ” λ©”μ„œλ“œμ—λŠ” λ„˜κΈΈ 수 μ—†λ‹€. μ΄λŠ” μ œλ„€λ¦­ ν•˜μœ„ νƒ€μž… κ·œμΉ™ λ•Œλ¬ΈμœΌλ‘œ List<String> 은 List의 ν•˜μœ„ νƒ€μž…μ΄μ§€λ§Œ List<Object>λŠ” μ•„λ‹ˆκΈ° λ•Œλ¬Έμ΄λ‹€. 

 

λ”°λΌμ„œ List<Object> 같은 λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ μ‚¬μš©ν•  λ•Œμ™€ 달리 List같은 λ‘œνƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ νƒ€μž… μ•ˆμ •μ„±μ„ μžƒκ²Œ λœλ‹€. 

 

 

λ˜ν•œ Setκ³Ό  Set<?> 차이 μ—­μ‹œ μ•ˆμ •μ„±μ— μžˆλ‹€. 

public class Numbers {
    static int numElementsInCommon(Set s1, Set s2) { // μ—­μ‹œ Set은 μ•ˆμ •μ„±μ΄ 깨진닀 (μ•„λ¬΄κ°’μ΄λ‚˜ 넣을 수 μžˆμ–΄μ„œ)
        int result = 0;
        for (Object o : s1) {
            if (s2.contains(o)) {
                result ++;
            }
        }
        return result;
    }

    // Set<?>으둜 μ„ μ–Έν•˜λ©΄ 무엇이든 받을 수 μžˆλŠ” Set νƒ€μž…μ΄ λœλ‹€.
    static int numElementsInCommon2(Set<?> s1, Set<?> s2) { // Set<?>λŠ” ν•œμ’…λ₯˜μ˜ νƒ€μž…λ§Œ 받을 수 μžˆλ‹€.
        int result = 0;
        for (Object o : s1) {
            if (s2.contains(o)) {
                result ++;
            }
        }
        return result;
    }

    public static void main(String[] args) {
        Set<Integer> set = new HashSet<>();
        Set<?> mySet = set;


        System.out.println(Numbers.numElementsInCommon(Set.of(1, 2 ,3), Set.of(1,2)));
        System.out.println(Numbers.numElementsInCommon2(Set.of(1, 2 ,3), Set.of(1,2)));
    }
}

Setκ³Ό Set<?> μ—­μ‹œ λΉ„μŠ·ν•˜κΈ΄ ν•˜μ§€λ§Œ Set은 μ•ˆμ •μ„±μ„ μžƒλŠ” λ‹€λŠ” μ μ—μ„œ 차이가 μžˆλ‹€. 

μ œλ„€λ¦­ νƒ€μž…μ„ μ“°κ³ λŠ” μ‹Άμ§€λ§Œ μ‹€μ œ νƒ€μž… λ§€κ°œλ³€μˆ˜κ°€ 무엇인지 μ‹ κ²½  μ“°κ³  싢지 μ•Šλ‹€λ©΄ μ™€μΌλ“œ μΉ΄λ“œλ₯Ό λŒ€μ‹  μ‚¬μš©ν•˜λŠ”κ²Œ μ’‹λ‹€. 

Set<?> 은 μ–΄λ–€ νƒ€μž…μ΄λΌλ„ 담을 수 μžˆλŠ” κ°€μž₯ λ²”μš©μ μΈ λ§€κ°œλ³€μˆ˜ν™” Set νƒ€μž…μ΄λ‹€. 

 

둜 νƒ€μž… μ»¬λ ‰μ…˜μ—μ„œλŠ” 아무 μ›μ†Œλ‚˜ 넣을 수 μžˆμ–΄ νƒ€μž… λΆˆλ³€μ‹μ„ ν›Όμ†ν•˜κΈ° μ‰¬μš΄ 반면, Collection<?>μ—λŠ” null μ™Έμ—λŠ” μ–΄λ–€ μ›μ†Œλ„ 넣을 수 μ—†λ‹€. 

 

 

λŒ€λΆ€λΆ„μ˜ κ²½μš°μ—μ„œ λ‘œνƒ€μž…μ΄ μ•„λ‹Œ λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ λ˜λŠ”λ° μ˜ˆμ™Έκ°€ μžˆλ‹€. 

λ°”λ‘œ class λ¦¬ν„°λŸ΄κ³Ό instanceof이닀. 

public class UseRawType<E> {
    private E e;

    public static void main(String[] args) {
        System.out.println("UseRawType.class = " + UseRawType.class);

        UseRawType<String> stringUseRawType = new UseRawType<>();

        System.out.println("stringUseRawType = " + (stringUseRawType instanceof UseRawType));
    }
}

UserRawType<Integer>.classλΌλŠ”κ²ƒμ€ μ—†κ³ , 클래슀λͺ….class만 κ°€λŠ₯ν•˜λ‹€.

그리고 instanceof도 λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ μ§€μ›ν•˜μ§€ μ•Šκ³  λ‘œνƒ€μž…μœΌλ‘œ μ‚¬μš©ν•΄μ•Ό ν•œλ‹€. 

 

 

 

πŸ“– 정리 

class λ¦¬ν„°λŸ΄κ³Ό instanceof μ˜ˆμ™Έλ₯Ό μ œμ™Έν•˜κ³ λŠ” 둜 νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ μ•ˆμ •μ„±κ³Ό ν‘œν˜„λ ₯을 μžƒκΈ° λ•Œλ¬Έμ— λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…μ„ μ‚¬μš©ν•˜μž!! 

둜 νƒ€μž…μ„ μ‚¬μš©ν•˜λ©΄ λŸ°νƒ€μž„ μ—λŸ¬κ°€ 일어날 수 있기 λ•Œλ¬Έμ— μ‚¬μš©ν•˜λ©΄ μ•ˆλœλ‹€. 둜 νƒ€μž…μ€ κ·Έμ € μ œλ„€λ¦­μ΄ λ„μž…λ˜κΈ° 전에 이전 μ½”λ“œμ™€μ˜ ν˜Έν™˜μ„±μ„ μœ„ν•΄ μ œκ³΅λ˜μ—ˆμ„ 뿐이닀! 

 

Set<Object>λŠ” μ–΄λ–€ νƒ€μž…μ˜ 객체도 μ €μž₯ν•  수 μžˆλŠ” λ§€κ°œλ³€μˆ˜ν™” νƒ€μž… 

Set<?> λŠ” λͺ¨μ’…μ˜ νƒ€μž… 객체만 μ €μž₯ν•  수 μžˆλŠ” μ™€μΌλ“œ μΉ΄λ“œ νƒ€μž… 

Sest 은 둜 νƒ€μž…μœΌλ‘œ μ œλ„€λ¦­ νƒ€μž… μ‹œμŠ€ν…œμ— μ†ν•˜μ§€ μ•ŠλŠ”λ‹€. 

 

Set<Object>와 Set<?> λŠ” μ•ˆμ „ν•˜μ§€λ§Œ, 둜 νƒ€μž…μ€ μ•ˆμ „ν•˜μ§€ μ•Šλ‹€. 

 

 

 ν•œκΈ€ μš©μ–΄ 영문 μš©μ–΄ 예
λ§€κ°œλ³€μˆ˜ν™” νƒ€μž…  paramterized type  List<String>
μ‹€μ œ νƒ€μž… λ§€κ°œλ³€μˆ˜ actual type paramteter String
μ œλ„€λ¦­ νƒ€μž… generic type List<E>
μ •κ·œ νƒ€μž… λ§€κ°œλ³€μˆ˜ formal type paramteter E
λΉ„ν•œμ •μ  μ™€μΌλ“œ μΉ΄λ“œ νƒ€μž… unbounded wildcard type List<?>
둜 νƒ€μž… raw type List
ν•œμ •μ  νƒ€μž… λ§€κ°œλ³€μˆ˜  bounded type paramter <E extends Number>
μž¬κ·€μ  νƒ€μž… ν•œμ • recursive type bound <T extends Comparable<T>>
ν•œμ •μ  μ™€μΌλ“œ μΉ΄λ“œ νƒ€μž… bounded wildcard type List<? extends Number>
μ œλ„€λ¦­ λ©”μ„œλ“œ  generic method static <E> List<E>
νƒ€μž… 토큰 type token String.class 

 

 

 

 

 

 

 

 

 

728x90