오늘 한 일

  • Java 공부
    • 상속은 언제 사용할까?
      하나의 클래스에 여러 특성을 한꺼번에 구현하는 경우 많은 코드에 많은 if문이 생길 수 있다. 이 때 하나의 클래스에 공통적인 요소를 모으고 나머지 클래스는 이를 상속받은 다음 각각 필요한 특성과 메서드를 구현하도록 하면 훨씬 깔끔하다.

      • IS-A 관계 (is a relationship : inheritance):
        일반적인(general) 개념과 구체적인(specific) 개념과의 관계
        상위 클래스: 일반적인 개념 클래스
        하위 클래스: 구체적인 개념 클래스
        단순히 코드를 재사용하는 목적으로 사용하지 않음
      • HAS-A 관계 (composition):
        한 클래스가 다른 클래스를 소유한 관계. 코드 재사용의 한 방법
        // Student가 Subject를 포함한 관계
        class Student {
          Subject majorSubject;
        }
        
    • 다운캐스팅 (down-casting)
      하위 클래스가 상위 클래스로 형 변환되는 것(업캐스팅)은 묵시적으로 이루어진다. 하지만 업캐스팅 된 인스턴스를 다시 원래 하위 클래스로 형 변환하려면 명시적으로 다운캐스팅을 해줘야 한다. 이때 인스턴스의 원래 타입을 체크하는 예약어가 instanceof이다.
      Animal hAnimal = new Human();
      if (hAnimal instanceof Human) {
        Human human = (Human)hAniman;
      }
      

      사실 이미 업캐스팅을 했는데 다시 다운캐스팅으로 되돌리는 것은 그리 좋은 방법은 아니다. 되도록이면 메서드 오버라이딩으로 해결하는 것을 우선으로 하자.

    • 추상 클래스 (abstact class)
      추상 메서드를 포함하는 클래스. 추상 메서드는 구현코드 없이 메서드의 선언만이 존재한다.
      추상 클래스와 추상 메서드는 abstract 예약어를 사용하며, 추상 클래스는 new (인스턴스 화) 할 수 없다. 추상 클래스라고 추상 메서드만 포함해야하는 것은 아니며 일반적인 메서드도 가질 수 있다. 가끔 다 구현되어 있지만 상속용으로만 사용하려고 추상 클래스로 선언하는 경우도 있다. cf) abstact class <-> concrete class
      abstract int add(int x, int y); // 선언만 았는 추상 메서드
      int add(int x, int y) {} // 추상 메서드 x
      
    • 템플릿 메서드
      싱글톤 패턴과 같이 디자인 패턴 중의 하나. 추상 메서드나 구현된 메서드를 활용하여 전체 기능의 흐름(시나리오)를 정의하는 메서드이다. final로 선언하면 하의 클래스에서 재정의할 수 없다.

      프레임 워크에서 많이 사용도되는 설계 패턴으로, 추상 클래스로 선언된 상위 클래스에 템플릿 메서드를 활용하여 전체적인 흐름을 정의하고 하위 클래스에서 다르게 구현되어야 하는 부분은 추상 메서드로 선언해서 하위 클래스가 구현하도록 한다.

      public abstract class Car {
        public abstract void drive();
        public abstract void stop();
            
        public void washCar() {}; // hook method. 본문 내용 x. 필요에 따라 override
            
        public void startCar() {
          System.out.println("시동을 겁니다.");
        }
            
        public void turnOff() {
          System.out.println("시동을 끕니다.");
        }
            
        final public void run() {
          startCar();
          drive();
          stop();
          washCar();
          turnOff();
        }
      }
      
    • final 예약어
      final 변수는 값이 변경될 수 없는 상수이다. final 변수는 오직 한번만 값을 할당할 수 있다.
      final 메서드는 하위 클래스에서 재정의(overriding) 할 수 없다.
      final 클래스는 더 이상 상속되지 않는다. ex) java의 String 클래스

    • 여러 자바 파일에서 공유하는 상수 값 정의 하기 프로젝트 구현 시 여러 파일에서 공유해야하는 상수 값은 하나의 파일에 선언하여 사용하면 편리하다.
      // 다른 클래스에서 Define.GOOD_MORNING 등으로 사용
      public class Define {
        final public static int MIN = 1;
        final public static int MAX = 99999;
        final public static int ENG = 1001;
        final public static int MATH = 2011;
        final public static double PI = 3.14;
        final public static String GOOD_MORNING = "Good Morning!";
      }
      
    • 인터페이스 (interface)
      • 인터페이스는 설계에 주로 사용된다. 인터페이스에는 상수와 추상 메서드만이 존재하고, 클래스는 이 인터페이스를 구현한다(implement). 인터페이스 내부에서 추상 메서드를 선언할 때는 public abstract 예약어를 생략해도 된다. 이후 pre-compile 단계에서 자동으로 추가된다. 상수 또한 public static final 예약어를 생략해도 후에 자동으로 추가된다.

      • 인터페이스를 상속 받는 것을 타입 상속, 추상 클래스를 상속 받는 것은 구현 상속이라는 표현을 사용한다. 이 또한 상속이기 때문에 자손 클래스의 인스턴스를 인터페이스 자료형으로 업캐스팅하는 것도 가능하다.

      • 인터페이스는 클래스 상속과는 달리 구현코드가 없기 때문에, 한 클래스에서 여러 인터페이스를 구현할 수 있다. (쉽게 말해 여러 인터페이스 상속 가능. implement 예약어 이후 쉼표로 구분)
        복수의 클래스를 상속했을 때 모호함이 발생하는 것(Diamond Problem)을 방지하기 위해 java는 이를 하나의 클래스 상속만은 허용한다.

        diamond_problem

        위 그림의 Class B와 C에서 각각 A의 메서드를 오버라이딩 했다고 하자. Diamond Problem이란 Class B와 C를 다중 상속받은 class D에서 이 메서드를 호출할 때 어떤 부모의 메서드를 불러와야 할지 모호해지는 문제를 말한다.

참조