Java

[Java] Covariant(곡변성)에 κ΄€ν•œ κ°„λ‹¨ν•œ 이야기

God Korea 2022. 9. 2. 01:34
728x90

πŸ“ 객체지ν–₯ 5원칙(SOLID)

 SOLIDλŠ” 객체지ν–₯ 5μ›μΉ™μ˜ λ‘μŒμ„ λ”°μ„œ λ§Œλ“  말이닀. 각각 λ‚˜μ—΄ν•˜μžλ©΄,

  • SRP(단일 μ±…μž„ 원칙)
  • OCP(개방 폐쇄 원칙)
  • LSP(λ¦¬μŠ€μ½”ν”„ μΉ˜ν™˜ 원칙)
  • ISP(μΈν„°νŽ˜μ΄μŠ€ 뢄리 원칙)
  • DIP(μ˜μ‘΄κ΄€κ³„ μ—­μ „ 원칙)

μœ„μ™€ 같이 μ‘΄μž¬ν•œλ‹€. 이 쀑 였늘 이야기할 Covariant(μ΄ν•˜ 곡변성)에 κ΄€ν•œ 이야기가 μ†ν•œ 곳은 LSP(Liskov Substitution Principle)λ‹€.

πŸ“Œ LSPλž€?

 LSPλŠ” νƒ€μž… Sκ°€ νƒ€μž… T의 μ„œλΈŒνƒ€μž…μ΄λΌλ©΄ ν”„λ‘œκ·Έλž¨μ˜ 속성 λ³€κ²½ 없이 Tνƒ€μž…μ˜ 객체λ₯Ό Sνƒ€μž…μœΌλ‘œ μΉ˜ν™˜ν•  수 μžˆμ–΄μ•Ό ν•œλ‹€.λΌλŠ” κ°œλ…μ΄λ‹€. μ‰½κ²Œ λ§ν•˜λ©΄, μ„œλΈŒνƒ€μž…μ€ μˆ˜νΌνƒ€μž…μ„ λŒ€μ²΄ν•  수 μžˆμ–΄μ•Ό ν•œλ‹€λŠ” 말이닀.

 μ„œλΈŒνƒ€μž…이 μˆ˜νΌνƒ€μž…μ„ λŒ€μ²΄ν•˜λŠ” κ°œλ…, 그것이 λ°”λ‘œ 곡변성이닀. 곡변성을 μ΄μš©ν•œ ν”„λ‘œκ·Έλž˜λ°μ˜ μž₯μ μ—λŠ” 크게(?) 3κ°€μ§€κ°€ μ‘΄μž¬ν•œλ‹€.

  1. λ‹€ν˜•μ„± κ΅¬ν˜„
  2. μ•ˆμ •μ„± 확보
  3. λΆˆν•„μš”ν•œ μ†ŒμŠ€ μ½”λ“œ μ ˆμ•½ (νƒ€μž… μΊμŠ€νŒ…)

 μ΄ν•΄λ₯Ό 돕기 μœ„ν•œ 예제λ₯Ό 보자. ν΄λž˜μŠ€κ°€ 총 4κ°œμ§€λ§Œ, κ΅¬ν˜„λœ μ†ŒμŠ€μ½”λ“œκ°€ κ°„λ‹¨ν•˜λ‹ˆ μ΄ν•΄ν•˜κΈ° μ–΄λ ΅μ§€λŠ” μ•Šμ„ κ±°λ‹€.

public class A {
    I method() {
        System.out.println("A");
        return new I();
    }
}
public class B extends A{
    J method() {
        System.out.println("B");
        return new J();
    }

    public static void test() {
        A a = new B();
        a.method();
    }

    public static void main(String[] args) {
        B.test();
    }
}
public class I {
}
public class J {
}

 μœ„와 같은 μ†ŒμŠ€ μ½”λ“œμ—μ„œ B class의 main λ©”μ„œλ“œλ₯Ό runν•˜λ©΄ λ‚˜μ˜€λŠ” 값은 λ¬΄μ—‡μΌκΉŒ?

더보기

java: method() in B cannot override method() in A
  return type J is not compatible with I

 μœ„와 같은 μ—λŸ¬κ°€ λ°œμƒν•œλ‹€. ν•΄μ„ν•˜λ©΄, B에 μžˆλŠ” λ©”μ„œλ“œλŠ” A에 μžˆλŠ” λ©”μ„œλ“œλ₯Ό μ˜€λ²„λΌμ΄λ“œ ν•  수 μ—†λ‹€.
κ·Έ μ΄μœ λŠ” Jνƒ€μž…μ€ Iνƒ€μž…κ³Ό ν˜Έν™˜λ˜μ§€ μ•ŠκΈ° λ•Œλ¬Έμ΄λ‹€.

 μž, 이제 μ—λŸ¬λ₯Ό ν•΄κ²°ν•˜λ €λ©΄ 무엇이 ν•„μš”ν•œκ°€? I와 J의 관계λ₯Ό μ„€μ •ν•΄μ£Όλ©΄ λœλ‹€.

public class J extends I {
}

 μœ„와 같이 Jλ₯Ό Iλ‘œλΆ€ν„° μƒμ†λ°›λŠ” 클래슀둜 λ§Œλ“€λ©΄ 더 이상 λ¬Έμ œκ°€ μ—†λ‹€. μ™œλƒν•˜λ©΄, JλŠ” Iλ₯Ό μ™„λ²½ν•˜κ²Œ λŒ€μ²΄ν•  수 있기 λ•Œλ¬Έμ΄λ‹€.(LSP 원칙) κ·Έλ ‡λ‹€λ©΄, μ•„λž˜μ™€ 같은 μ˜ˆμ œλŠ” μ–΄λ–€κ°€? μ•„λž˜ 4개의 λ©”μ„œλ“œ 쀑 μ–΄λ–€ 것이 잘λͺ»λœ 것인지 μ•Œ 수 μžˆλ‹€λ©΄, 당신은 곡변성을 μ΄ν•΄ν–ˆλ‹€.

public class B extends A{
    J method() {
        System.out.println("B");
        return new J();
    }

    public static I one() {
        A a = new B();
        return a.method(); // return I
    }

    public static I two() {
        B b = new B();
        return b.method(); // return J
    }

    public static J three() {
        A a = new B();
        return a.method(); // return I
    }

    public static J four() {
        B b = new B();
        return b.method(); // return J
    }

    public static void main(String[] args) {
        B.one();
        B.two();
        B.three();
        B.four();
    }
}

 

 μ •닡은 3번 three() λ©”μ„œλ“œκ°€ μ—λŸ¬κ°€ λ°œμƒν•œλ‹€. κ·Έ μ΄μœ λŠ” 주석에도 보이듯이 J νƒ€μž…μ„ λ°˜ν™˜ν•΄μ•Ό ν•˜λŠ” λ©”μ„œλ“œμ—μ„œ a.method()λŠ” I νƒ€μž…μ„ λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€.

 μ—¬κΈ°μ„œ ν•˜λ‚˜ 짚고 λ„˜μ–΄κ°€μ•Όν•˜λŠ” 것은 LSPλŠ” 곡변성을 μΈμ •ν•˜μ§€, 역곡변성은 μΈμ •ν•˜μ§€ μ•ŠλŠ”λ‹€. 즉, μˆ˜νΌνƒ€μž…μ€ μ„œλΈŒνƒ€μž…μ„ μ™„μ „νžˆ λŒ€μ²΄ν•  ν•„μš”κ°€ μ—†λ‹€. 즉, Iλ₯Ό μƒμ†λ°›λŠ” JλŠ” Jκ°€ Iνƒ€μž…κ³Ό κ°™λ‹€κ³  봐도 λ¬΄λ°©ν•˜μ§€λ§Œ, Iκ°€ Jνƒ€μž…κ³Ό κ°™λ‹€κ³ λŠ” λ³Ό 수 μ—†λ‹€.

 μ΄λŠ” 동물에 λΉ„μœ ν•˜λ©΄ 쉽닀. 동물과 ν˜Έλž‘μ΄ ν΄λž˜μŠ€κ°€ μžˆλ‹€κ³  κ°€μ •ν•˜μž. ν˜Έλž‘μ΄λŠ” 동물인가? κ·Έλ ‡λ‹€. κ·Έλ ‡λ‹€λ©΄, 동물은 ν˜Έλž‘μ΄μΈκ°€? 이 말은 λΆ„λͺ…ν•œ λͺ¨μˆœμ΄ μžˆλ‹€. 동물은 ν˜Έλž‘μ΄λ„ 될 수 μžˆμ§€λ§Œ, λ‹€λ₯Έ 동물도 될 수 있기 λ•Œλ¬Έμ΄λ‹€. 역곡변성을 μΈμ •ν•˜μ§€ μ•ŠλŠ” 것은 이 μ˜ˆμ‹œλ‘œ μ„€λͺ…이 λœλ‹€. JλŠ” I에 속해 μžˆμ§€λ§Œ, IλŠ” Jκ°€ 될 μˆ˜λ„ 있고 λ‹€λ₯Έ 것이 될 μˆ˜λ„ 있기 λ•Œλ¬Έμ— λ”± μ§€μ •ν•  수 μ—†λ‹€.

 λ˜ν•œ, threeλ©”μ„œλ“œλ₯Ό 보면 λΆ„λͺ…νžˆ B μƒμ„±μžλ₯Ό 톡해 객체λ₯Ό μƒμ„±ν–ˆλŠ”λ° μ™œ Jκ°€ μ•„λ‹Œ Iλ₯Ό λ¦¬ν„΄ν•˜λŠ”μ§€ κΆκΈˆν•  수 μžˆλ‹€. 이것은 μƒμ„±μžλ³΄λ‹€ λ³€μˆ˜μ˜ νƒ€μž…μ΄ 더 μ€‘μš”ν•œλ°, λ³€μˆ˜μ˜ νƒ€μž…μ΄ A이기 λ•Œλ¬Έμ— Aμ—μ„œ μƒμ„±λœ 리턴값을 λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ΄λ‹€. (그런데, ν”„λ¦°νŠΈ κ°’μ—λŠ” Bκ°€ μ°νžˆλŠ”λ°, 이것에 λŒ€ν•œ μ •ν™•ν•œ μ΄μœ λŠ” λͺ¨λ₯΄κ² λ‹€. λ‹€λ§Œ, ν˜„μž¬ λ‚˜λŠ” 객체가 λ¦¬ν„΄ν•˜λŠ” κ°’κ³Ό λ‹¨μˆœν•œ λ‘œμ§μ„ μ²˜λ¦¬ν•˜λŠ” ν”„λ¦°νŠΈλ¬Έμ€ μ„œλ‘œ μ²˜λ¦¬λ˜λŠ” μˆœμ„œκ°€ λ‹¬λΌμ„œ λ°œμƒν•˜λŠ” 것이라고 μƒκ°ν•˜κ³  μžˆλ‹€.) ps. ν˜Ήμ‹œ μ •ν™•ν•œ 이유λ₯Ό μ•Œκ³  κ³„μ‹œλ‹€λ©΄ λŒ“κΈ€μ„ λΆ€νƒλ“œλ¦½λ‹ˆλ‹€.

 μ—¬ν•˜νŠΌ, three λ©”μ„œλ“œλ₯Ό ν•΄κ²°ν•˜λŠ” 방법 쀑 ν•˜λ‚˜λŠ” νƒ€μž… μΊμŠ€νŒ…μ΄ μžˆλ‹€. return a.method(); μ•žμ— (J)λ₯Ό λΆ™μ—¬μ£Όλ©΄ λœλ‹€. 즉, return (J)a.method();κ°€ λ˜λŠ” 것이닀. κ·Έλ ‡κ²Œ ν•˜λ©΄ λ°˜ν™˜ν•˜λŠ” 값이 (J)둜 νƒ€μž… μΊμŠ€νŒ…μ΄ 되게 λœλ‹€. λ‹€λ§Œ, μ΄λ ‡κ²Œ νƒ€μž… μΊμŠ€νŒ…μ΄ λ‚œλ¬΄ν•˜κ²Œ 되면 κ°œλ°œμžκ°€ μ‹€μˆ˜λ₯Ό ν–ˆμ„ λ•Œ, 였λ₯˜λ₯Ό μž‘κΈ°κ°€ μ–΄λ ΅λ‹€. κ·Έ μ΄μœ λŠ” 곡변성에 μ˜ν•œ νƒ€μž… μΊμŠ€νŒ…μ€ 컴파일 μ—λŸ¬λ₯Ό λ°˜ν™˜ν•˜κΈ° λ•Œλ¬Έμ— μ†ŒμŠ€ μ½”λ“œ μžμ²΄μ—μ„œ μ—λŸ¬ λ‚΄μš©μ΄ 보이고, ν”„λ‘œκ·Έλž¨μ΄ λ™μž‘ν•˜μ§€ μ•Šμ§€λ§Œ, 일반적인 νƒ€μž… μΊμŠ€νŒ…μ— μ˜ν•œ μ‹€μˆ˜λŠ” λŸ°νƒ€μž„ μ—λŸ¬λ₯Ό λ°˜ν™˜ν•˜λŠ” κ²½μš°κ°€ λŒ€λΆ€λΆ„μ΄λΌμ„œ μ—λŸ¬ λ‚΄μš©μ„ 찾기도 μ–΄λ ΅κ³ , ν”„λ‘œκ·Έλž¨μ΄ λ™μž‘ν•œ 후에 μ—λŸ¬κ°€ λ°œμƒν•˜κ²Œ λ˜μ–΄ μƒλ‹Ήν•œ μœ„ν—˜μ΄ μžˆλ‹€.

 κ·ΈλŸ¬λ―€λ‘œ, 객체지ν–₯ μ–Έμ–΄μ—μ„œλŠ” λŒ€λΆ€λΆ„μ˜ κ²½μš°μ—μ„œ LSP(λ¦¬μŠ€μ½”ν”„ μΉ˜ν™˜ 원칙)에 μ˜ν•œ 코딩을 μ§€ν–₯ν•˜λŠ” 바이닀.

728x90