[TIL] 코드스테이츠 SEB BE Day 13

💡 Today I Will Learn

  • 다형성
  • 추상화

✏️ Summary


  • 다형성(Polymorphism)

    세번째 OOP 핵심인 다형성은 객체를 여러형태로 표현할 수 있다는 것을 의미한다.
    자바에서의 다형성은 한 타입으로부터 여러 타입의 객체를 참조 가능하게 한다.
    아무 타입이나 참조하는 것이 아닌 상위 클래스에 다양한 하위 클래스가 참조될 수 있다는 것이다.

    즉, 참조변수가 사용하는 멤버의 수는 인스턴스화한 객체의 멤버의 수보다 같거나 적을 것이다. (상속을 생각해보자)

    class Super{
    
    }
    
    class This1 extends Super{
    
    }
    
    class This2 extends Super{
    
    }
    
    public class Test{
      public static void main(String[] args){
        Super super = new Super();
    
        Super this = new This1();
    
        this = new This2();
    
        This1 super = new Super(); // 불가능
      }
    }
    

    하위 클래스의 객체로 상위 클래스를 인스턴스화 할 수 없다. Super의 멤버개수보다 This1의 멤버개수가 많기 때문이다.
    다형성 또한 코드의 중복을 줄여 효율적이다. 또한 메서드 오버라이딩과 오버로딩은 이 다형성을 잘보여주는 예이다. 반복적으로 비슷한 코드[보일러플레이트(boilerplate) 코드]를 최소화 할 수 있다.

    • 참조변수의 타입변환

      자료형의 형변환과 마찬가지로 타입변환이 가능하다.
      형변환에서는 자료형 Type을 변환하는 의미였다면, 여기서는 멤버 개수를 조절하는 의미이다.

      public class Super {}
      class This extends Super{}
      
      class Test{
          public static void main(String[] args) {
              Super testSuper = new This(); // 업캐스팅
              This testThis = (This) testSuper; // 다운캐스팅
          }
      }
      
      1. 상위클래스와 하위클래스 사이에서만 타입변환이 가능 (상속관계)
      2. 하위클래스에서 상위클래스로의 업캐스팅은 괄호 생략 가능
      3. 상위클래스에서 하위클래스로의 다운캐스팅은 반드시 괄호 명시
    • instanceof

      캐스팅의 여부를 확인하려면 ‘해당 객체를 어떤 클래스로 인스턴스화 하였나’ 그리고 ‘상속관계가 존재하는가’ 여부가 필요하다.

      확인대상(객체) instanceof 검사대상 클래스(포함여부)

      public class Super{}
      class This extends Super{}
      class That{}
      
      class Test{
          public static void main(String[] args) {
              Super aSuper = new Super();
              System.out.println(aSuper instanceof Super); // true
              System.out.println(aSuper instanceof This); // false
                  
              Super bSuper = new This();
              System.out.println(bSuper instanceof This); // true
              System.out.println(bSuper instanceof That); //false
          }
      }
      

      코드가 복잡할 때, 실제 객체의 타입과 인스턴스화된 타입의 차이를 확인하기 어렵기에 instanceof 연산자를 사용한다.

    • 다형성의 활용

      public class Person {
        int age;
        String name;
      
        public int getAge() {
            return age;
        }
      
        public void setAge(int age) {
            this.age = age;
        }
      
        public Person(int age, String name) {
            this.age = age;
            this.name = name;
        }
      
        public Person(){}
      
        public int older(Person person) {
            person.setAge(person.getAge()+1);
      
            return person.getAge();
        }
      }
      
      class Singer extends Person{
          String groupName;
      
          public Singer(int age, String name, String groupName) {
              super(age, name);
              this.groupName = groupName;
          }
      
          @Override
          public String toString() {
              return "Singer";
          }
      }
      
      class Student extends Person{
          int grade;
      
          public Student(int age, String name, int grade) {
              super(age, name);
              this.grade = grade;
          }
      
          @Override
          public String toString() {
              return "Student";
          }
      }
      
      class Test{
          public static void main(String[] args) {
              Person person = new Person();
                  
              System.out.println("나는 22살인데, "+person.older(new Singer(22, "김가수", "밴드"))+"살이 되었어요.");
              System.out.println("나는 12살인데, "+person.older(new Student(12, "김학생", 6))+"살이 되었어요.");
      
          }
      }
      

      다형성을 이용하여 각각 다른 타입의 매개변수를 갖는 여러 메서드를 생성하는 것이 아닌, 상위 클래스를 매개변수로 설정하여 하나의 메서드를 이용하는 예시를 들어보았다.

  • 추상화 (Abstraction)

    마지막 OOP 핵심개념인 추상화는 공통적인 속성과 기능을 추출하여 정의하는 것이다.
    즉, 여러 클래스들의 공통적인 것을 모아서 새로운 상위 클래스를 만드는 것이다. (반대로 상위 클래스를 만들고 하위 클래스들을 정의하는 것도 가능하다.)

    추상화_다이어그램

    자동차와 비행기가 Move라는 공통적인 기능을 묶어 코드의 중복을 줄인 다음, 자동차는 땅에서, 비행기는 하늘에서라는 구체적인 구현이 필요하다.

    • abstract 제어자

      자바에서의 abstract는 ‘미완성’을 의미한다. abstract classabstract method를 정의할 수 있다.

      abstract class Test{ // 추상 클래스
          abstract void method(); // 추상 메서드
      }
      

      메서드의 시그니처만 존재하고, 구현부가 없으면 method 제어자에 abstract를 명시하여 ‘추상 메서드’로 사용한다.
      이 추상 메서드를 포함한 클래스에 abstract 제어자를 명시하여 ‘추상 클래스’로 이용한다.

    • 추상 클래스

      미완성된 메서드인 추상메서드를 포함하는 추상 클래스는 해당 메서드를 구체화하지 않는 이상 객체를 생성하는 것이 불가능하다.
      그럼에도 사용하는 이유는 다음과 같다.

      1. 상속 관계에서 새 클래스를 작성하는 데 유용하다.
        하위 클래스에 따라 기능이 달라질 수 있기에 상위 클래스에서는 선언만 담당하고 하위 클래스에서 구현하도록하여 상황에 유연하게 대처할 수 있다.
         public abstract class Vehicle {
         public abstract void move();
        
         }
        
         class Car extends Vehicle{
             @Override
             public void move() {
                 System.out.println("Run Ground");
             }
         }
        
         class Plane extends Vehicle{
             @Override
             public void move() {
                 System.out.println("Move Sky");
             }
         }
        

        move() 메서드를 추상화하여 각 하위 클래스에 따라 각각 Overriding을 통해 구체화 하는 코드이다.
        즉, 공통된 기능을 각 상황에 맞게 구체화하는 것이다.

      2. 추상화를 구현하는 데 핵심적인 역할을 한다.
        팀에서 개발을 할때, 공통된 속성과 기능을 서로 다른 메서드와 변수로 정의되는 오류를 미리 방지할 수 있다.
  • final

    ‘최종’, ‘마지막’ 이라는 뜻을 가졌지만 선언 위치에 따라 사용되는 의미가 다르다.

    위치의미
    클래스변경 및 확장이 불가능, 상속 불가능
    메서드오버라이딩 불가능(상속 불가능)
    변수값 변경이 불가능(constant)

    변경이 불가능하다는 공통점이 있고, 추상화에서는 구체화가 불가능한 것이므로 유념하도록 한다.

  • 인터페이스

    추상 클래스와 마찬가지로 OOP의 추상화를 구현한다.
    다만, 추상 클래스보다 더 추상화시킬 수 있다. 추상 클래스는 추상메서드와 멤버 변수를 포함할 수 있지만, 인터페이스는 오로지 추상 메서드상수(constant) 만을 멤버로 가질 수 있다.

    public interface Test{
      public static final v1 = 0;
      final int v2 = 1;
      static int v3 = 2;
    
      public abstract void method();
      void method1();
    }
    

    인터페이스의 정의는 interface 키워드를 사용한다.
    상수는 public static final로 정의하고, 메서드는 public abstract로 정의한다.
    다만 위처럼 생략한 부분이 있다면 컴파일러가 자동으로 추가한다.

    • 인터페이스의 구현

      class Test implements Inter1, Inter2{
        ....
      }
      

      implements 키워드로 인터페이스를 구현하며, 상속과 달리 다중으로 구현이 가능하다.

      abstract class Animal{
          abstract void cry();
      }
      
      interface Pet{
          static int v=1;
          final int v1=2;
      
          void play();
      }
      
      class Dog extends Animal implements Pet {
          @Override
          void cry() {
              System.out.println("멍멍");
          }
      
          @Override
          public void play() {
              System.out.println("뛰어놀기");
          }
      }
      
      class Cat extends Animal implements Pet {
          @Override
          void cry() {
              System.out.println("야옹");
          }
      
          @Override
          public void play() {
              System.out.println("쥐잡기");
          }
      }
      
      public class PetTest {
          public static void main(String[] args) {
              Dog dog = new Dog();
              Cat cat = new Cat();
          }
      }
      

      상속과 인터페이스 구현을 동시에 하는 코드를 구성해보았다. 상속에서 상위 클래스에 동일한 이름의 필드나 메서드가 있으면 충돌이 발생하는데, 인터페이스는 미완성된 멤버를 가지고 있어 충돌 발생 여지가 없다. 그러므로 인터페이스는 다중 구현이 가능한 것이다.

📌 정리


다형성과 추상화는 OOP 핵심 중에서도 중요하게 다뤄지는 부분이다. 개념 자체가 추상?적이어서 머리속으로만 이해하고 넘어가면 아주 큰일난다는 것을 알고 있다. 그래서 코드를 IDE에 직접 작성하며 되는 케이스와 안되는 케이스를 일일히 확인하였다.
오는 주말에는 배웠던 OOP에 대해 복습하고, 다음주에 배울 개념들을 미리 예습하여 시간에 쫓기지 않게 준비해야겠다😎

👿 Problem

하위 클래스를 상위 클래스가 상속받지 못하는 이유가 멤버변수의 개수 때문?

👼 Solution

상속을 받은 하위 클래스의 경우 생성자에서 super()를 통해 상위 클래스의 멤버변수를 초기화 한다. 반대로 객체가 하위 클래스인데, 상위 클래스가 인스턴스화 되었다면 하위 클래스에서 호출하는 멤버를 상위 인스턴스에서 호출할 수 있다는 보장이 없기 때문에 불가능하다. 즉, 하위 클래스의 멤버가 상위 클래스보다 같거나 많기 때문에 보장이 안되어서 불가능하다는 것이다.

🎯 Next Week


  • 심화 학습

Back to [TIL] 코드스테이츠 SEB BE Day 12