Java 프로그래밍 : 객체지향 (3)

상속 (extends)

상속을 사용하면 코드 중복을 제거하고, 기존 클래스를 확장하기 쉬워 프로그램 확장성을 증가시킬 수 있다. class A extends B {} 와 같은 형식으로 사용한다. B 클래스를 A가 상속받는 것이다. 이 경우 B가 부모 클래스, A가 자식 클래스가 된다. 해당 클래스의 필드는 물론이고 메소드를 그대로 사용할 수 있다.

업 캐스팅

업캐스팅이란, 자식 객체를 부모의 타입으로 해석하는 것이다. 예를 들어 아래와 같은 상속 관계가 있을 때, Cat의 인스턴스(객체)는, Animal로 해석 될 수 있다.

class Animal { ... }
class Cat extends Animal { ... }
class Dog extends Animal { ... }
class Horse extends Animal { ... }

하지만 위 내용의 역은 성립하지 않으니 주의해야한다.
(고양이는 동물이다 O, 동물은 고양이다 X)

// 고양이 객체 생성
Cat c = new Cat();
// 고양이는 동물이다(O)
Animal a = c; // 고양이 객체를 동물로 해석 

// 동물 객체 생성
Animal aaa = new Animal();
// 동물은 고양이다(X)
Cat ccc = aaa; // ERROR!

그룹화

이러한 업 캐스팅은, 다양한 객체들을 부모의 타입으로 관리할 수 있게 한다.

Animal c = new Cat();   // 고양이는 동물이다
Animal d = new Dog();   // 개는 동물이다
Animal h = new Horse(); // 말은 동물이다
// 동물 배열 - 고양이, 개, 말
Animal[] animals = { c, d, h };

오버라이딩(overriding)

메소드 오버라이딩(overriding)이란 부모의 메소드를 자식 클래스에서 다시 정의하는 것이다. 예를 들어 부모 클래스(A)에서 공격 메소드 데미지가 10인데, 자식 클래스(B)에서는 데미지를 30으로 늘리고자 한다. 이때 사용하는 개념이 메소드 오버라이딩이다. 아래의 B 클래스는 부모 A 클래스의 attack() 메소드를 새롭게 재정의한다.

class A {
  public void attack() {
    System.out.println("데미지 10 !");
  }
}

class B extends A {
  // 메소드 오버라이딩(재정의)
  public void attack() {
    System.out.println("데미지 30 !");
  }
}

protected 접근제어자

protected 접근제어자는 상속 관계의 클래스까지 접근을 허용한다. 아래 코드의 필드 name은 protected 선언되었으므로, B에서 직접 사용할 수 있다.

class A {
  protected String name;
}
class B extends A {
  public void printName() {
    // 부모클래스 A의 필드 name을 출력
    System.out.println(name);
  }
}

super - 상속과 생성자

자식 객체를 생성과 동시에 초기화기위해서는 반드시, 부모의 생성자가 먼저 호출되어야만 한다. 이때 super 를 사용한다.

public class SuperTest {
  public static void main(String[] args) {
    
    User usr = new User("회원",1);
    System.out.println(usr.toString());
    
    BestUser bestUsr = new BestUser("명예회원",10,3);
    System.out.println(bestUsr.toString());
  }
}

class User {
  protected String name;
  protected int lv;
  
  public User(String name, int lv) {
    this.name = name;
    this.lv = lv;
  }
  
class BestUser extends User {
  int points;
  
  public BestUser(String name, int lv, int points) {
    super(name, lv);
    this.points = points;
  }

인터페이스

인터페이스란 메소드 묶음의 역할 정의 방법이다. 내용이 없는 껍데기 메소드인 프로토타입 메소드를 먼저 선언하고, implements 키워드를 사용해 클래스에게 역할을 부여한다. 인터페이스의 장점은 업캐스팅이 가능하다는 점과, 프로그램 설계의 명확성이 증가한다는 것이다.

public class SmartPhoneTest {
  public static void main(String[] args) {
 
    SmartPhone sp = new SmartPhone();
    
    sp.ring();
  }
}

interface Alarm {
  public void beep();
}

interface Camera {
  public void shot();
}

class SmartPhone implements Alarm, Camera {
  public void beep(){
    System.out.print("띠링 띠링-!");
  }
  
  public void Camera(){
    System.out.print("찰칵-!");
  }
}

다형성

위에서 설명한 인터페이스 예시처럼, 휴대폰은 알람이기도 하고 동시에 카메라이기도 하다. 이렇게 하나의 객체가 다양한 타입으로 해석되는 것이 다형성(Polymorphism)이다. SmartPhone 클래스는 아래 코드와 같이 쓰일 수도 있다.

Alarm a = new SmartPhone();
Camera c = new SmartPhone();

🙏 참고 강의

좋은 웹페이지 즐겨찾기