상속을 사용하면 코드 중복을 제거하고, 기존 클래스를 확장하기 쉬워 프로그램 확장성을 증가시킬 수 있다. 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!
부모 타입으로 업캐스팅한 객체를 원래 타입으로 돌려놓는 것을 다운캐스팅이라고 한다.
다운 캐스팅 하는 방법은 (원래타입) 다운캐스팅할 변수명
이다.
실제 예시는 아래와 코드를 참고하자.
class Animal { ... }
class Cat extends Animal { ... }
class Dog extends Animal { ... }
class Horse extends Animal { ... }
// 고양이 객체 생성
Cat c = new Cat();
Animal a = c; // 업캐스팅 :
Cat cc = (Cat) c; //다운캐스팅
이러한 업 캐스팅은, 다양한 객체들을 부모의 타입으로 관리할 수 있게 한다.
Animal c = new Cat(); // 고양이는 동물이다
Animal d = new Dog(); // 개는 동물이다
Animal h = new Horse(); // 말은 동물이다
// 동물 배열 - 고양이, 개, 말
Animal[] animals = { c, d, h };
메소드 오버라이딩(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 접근제어자는 상속 관계의 클래스까지 접근을 허용한다. 아래 코드의 필드 name은 protected 선언되었으므로, B에서 직접 사용할 수 있다.
class A {
protected String name;
}
class B extends A {
public void printName() {
// 부모클래스 A의 필드 name을 출력
System.out.println(name);
}
}
자식 객체를 생성과 동시에 초기화기위해서는 반드시, 부모의 생성자가 먼저 호출되어야만 한다. 이때 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 ring();
}
interface Camera {
public void shot();
}
class SmartPhone implements Alarm, Camera {
public void ring(){
System.out.print("띠링 띠링-!");
}
public void Camera(){
System.out.print("찰칵-!");
}
}
위에서 설명한 인터페이스 예시처럼, 휴대폰은 알람이기도 하고 동시에 카메라이기도 하다. 이렇게 하나의 객체가 다양한 타입으로 해석되는 것이 다형성(Polymorphism)이다. SmartPhone
클래스는 아래 코드와 같이 쓰일 수도 있다.
Alarm a = new SmartPhone();
Camera c = new SmartPhone();
안녕하세요. SmartPhone인스턴스를 Alarm타입의 참조변수로 참조하면 장점이 무엇인가요?