상속
상속은 클래스끼리 부모와 자식을 정해서 자식이 부모의 메소드들을 상속받아 활용할 수 있는 방식이다.
사전 정의 그대로 우리는 모두 부모님의 특징을 그대로 물려받고, 거기에 더해서 자식만의 개성이 나타나듯이, 자식클래스는 부모클래스의 모든 함수들을 그대로 물려받아 쓰고, 거기에 본인만의 변수와 메소드를 추가해서 활용할 수 있다.
부모클래스와 자식클래스가 상속관계를 주고받는 방법을 알아보자.
package family;
public class Parents {
protected int x; // 외부에서 접근은 막으면서 상속은 해주는 protected 제한자
protected int y;
protected char z;
public void sub() {
System.out.println("화요일입니다.\n");
}
}
부모클래스의 멤버변수에 새로운 접근제한자 protected가 보인다. protected 제한자는 외부에서 접근을 못하게 하는 특징은 private과 닮았고, 공유할 수 있는 범위를 자식클래스로 한정하는 면에선 public과 닮았다고 할 수 있다. 정확히 private과 public사이의 제한자이다.
package family;
public class Son extends Parents { // x, y, z, sub()를 상속받음
private int a;
private int b;
public Son(int x, int y, char z, int a, int b) {
super.x=x; // super.x도 가능하다
super.y=y; // this.y=y;
super.z=z; // this.z=z;
this.a=a;
this.b=b;
}
public void disp() {
System.out.println(x); // private은 상속을 안해준다
System.out.println(this.x);
System.out.println(super.x); // super는 부모의 것
System.out.println(y);
System.out.println(this.y);
System.out.println(super.y);
System.out.println(z);
System.out.println(this.z);
System.out.println(super.z);
System.out.println(a);
System.out.println(b);
}
}
부모클래스를 상속하려면 extends 뒤에 부모클래스를 붙이면 된다.
그리고 super. 키워드가 등장하는데, super. 는 자식클래스가 아니라 부모클래스를 지칭하는 키워드이다.
실행클래스를 만들어서 콘솔에 표시해보자.
package family;
public class Ex {
public static void main(String[] args) {
Son son=new Son(1, 2, 'A', 88, 77);
son.sub();
son.disp();
}
}
메소드뿐만 아니라 생성자 또한 상속할 수 있다.
package inheritance;
public class Parents {
protected int x;
protected int y;
public Parents() {
this.x=88;
this.y=99;
}
public Parents(int x, int y) {
this.x=x;
this.y=y;
}
}
** 생성자 중복으로 default 생성자와 정수형 자료형 x와 y를 입력받았을 때의 생성자가 같이 정의되어있다.
package inheritance;
public class Son extends Parents {
private char z;
public Son() {
super(); // 부모의 default 생성자를 쓴다 Parents()
this.z='A';
}
public Son(int x, int y, char z) {
super(x, y);
this.z=z;
}
public void disp() {
System.out.println(x + "\t" + y + "\t" + z);
}
}
super. 키워드로 부모의 생성자를 그대로 가져다 쓸 수 있다.
package inheritance;
public class Ex {
public static void main(String[] args) {
Son son=new Son();
son.disp();
Son son1=new Son(1, 2, '*');
son1.disp();
}
}
※ 함수 재정의(Overriding)
부모클래스의 메소드를 받았는데 그대로 쓰기엔 애매하다면 조금 고쳐쓸 수 있는 방법이 있다.
부모클래스의 메소드를 자식클래스에서 재정의해서 쓰게 된다면, 부모 클래스로 호출한 메소드와 자식클래스로 호출한 메소드는 이름이 같더라도 각각의 역할을 수행한다.
package overloading;
public class Parents {
public void aa() {}
public void bb() {}
public void cc() {}
public void dd() {}
public void ee() {}
public void disp() {
System.out.println("Parents Class");
}
}
package overloading;
public class Son extends Parents { // aa()~disp()까지 무조건 받음
// 함수재정의(Overriding)
public void disp() {
System.out.println("Son Class");
}
}
package overloading;
public class Ex {
public static void main(String[] args) {
Parents p=new Parents();
p.disp();
Son s=new Son();
s.disp();
}
}
이렇게 재정의된 함수를 부모클래스는 형 변환을 통해 담아줄 수 있다.
이를 Upcasting이라고 하며, 반대로 자식클래스가 부모클래스의 함수를 호출하는걸 Downcasting이라고 한다.
Upcasting을 할 땐 형 변환을 생략해도 되지만 반대의 경우엔 생략하면 안 된다.
package overloading;
public class Ex {
public static void main(String[] args) {
Parents parent=new Parents(1,3);
parent.calculate();
Son son=new Son(10, 20);
son.calculate();
Parents s=new Son(77, 88);
s.calculate();
Son c=(Son) s;
c.calculate();
}
}
또한 Downcasting은 Upcasting 된 대상에게만 해줄 수 있다.
※ 추상클래스(abstract)
일반클래스 상속 시엔 overloading은 선택이다. 하지만 overloading을 강제해줄 수 있는 클래스가 추상클래스(abstract class)이다.
부모클래스에서 정의만 하고 구현하지 않은 다음, 각 자식클래스에서 강제적으로 재정의해준다.
package abs;
public abstract class Parents { //추상클래스
protected int x;
protected int y;
public void msg() {
System.out.println("hahaha");
}
public abstract void disp(); // 추상함수 - 함수선언, 반드시 재정의가 필요함
}
추상메소드가 하나라도 있는 클래스는 추상클래스가 된다.
disp()라는 추상메소드를 정의만 해놓고 구현은 하지 않는다.
package abs;
public class Son extends Parents { // x, y, msg() 쓸 수 있음
public void disp() { // 강제 재정의
System.out.println("hohohoho");
}
public void apple() {
System.out.println("apple function");
}
}
package abs;
public class Ex {
public static void main(String[] args) {
// Parents a=new Parents(); ERROR
Son s=new Son();
s.msg();
s.disp();
s.apple();
Parents p=new Son();
p.msg();
p.disp();
// p.apple(); ERROR
}
}
추상클래스는 직접 객체를 만들어서 사용할 수 없다.
추상클래스의 용도는 예를 들어 설명하면 쉽게 다가온다.
3가지 모델의 로봇에 대한 데이터를 표현하고자 한다: 고성능의 super로봇, 표준형의 standard로봇, 그리고 저가형의 low로봇.
로봇들은 공통적으로 머리, 팔, 다리, 몸통으로 구성이 되어있고, 모델마다 차이점은 미사일 탑재 여부, 칼 탑재 여부, 비행 가능 여부이다.
먼저 로봇의 공통점을 담은 Robot 클래스를 만들어보자.
package robot_model;
public abstract class Robot {
protected String model;
public void shape() {
System.out.println(model + "은 팔, 다리, 몸통, 머리가 있습니다");
}
public void walk() {
System.out.println("걸을 수 있습니다");
}
public void run() {
System.out.println("달릴 수 있습니다");
}
public abstract void flight();
public abstract void missile();
public abstract void sword();
}
모든 로봇들이 공통적으로 가지는 특징은 일반 메소드로 표현해서 상속시켜주고, 각각 다르게 재정의되어야 하는 기능들에 대해서는 추상메소드를 만들었다. 어차피 재정의해야 하기 때문에, 빈칸으로 남김으로써 초기값을 메모리에 저장하지 않아도 되는 장점이 있다.
package robot_model;
public class SuperRobot extends Robot {
public SuperRobot(String model) {
super.model=model;
}
public void flight() {
System.out.println("날 수 있습니다");
}
public void missile() {
System.out.println("미사일을 쏠 수 있습니다");
}
public void sword() {
System.out.println("레이저검을 가지고 있습니다");
}
}
package robot_model;
public class StandardRobot extends Robot {
public StandardRobot(String model) {
super.model=model;
}
@Override
public void flight() {
System.out.println("날 수 없습니다");
}
@Override
public void missile() {
System.out.println("미사일을 쏠 수 있습니다");
}
@Override
public void sword() {
System.out.println("목검을 가지고 있습니다");
}
}
package robot_model;
public class LowRobot extends Robot{
public LowRobot(String model) {
super.model=model;
}
@Override
public void flight() {
System.out.println("날 수 없습니다");
}
@Override
public void missile() {
System.out.println("미사일을 쏠 수 없습니다");
}
@Override
public void sword() {
System.out.println("검을 가지고 있지 않습니다");
}
}
이렇게 부모클래스 Robot으로부터 상속받은 3가지의 추상메소드들을 각자 특징에 맞게 재정의해줬다.
package robot_model;
public class Ex {
public static void sub(Robot robot) { // robot=sup(SuperRobot)
robot.shape();
robot.walk();
robot.run();
robot.flight();
robot.missile();
robot.sword();
System.out.println("============================");
}
public static void main(String[] args) {
SuperRobot sup=new SuperRobot("Super Robot");
sub(sup);
StandardRobot standard=new StandardRobot("Standard Robot");
sub(standard);
LowRobot low=new LowRobot("Low Robot");
sub(low);
}
}
한 단계 더 간소화하기 위해 각 로봇 모델의 특징을 나열해주는 sub() 메소드를 활용했다. sub()메소드는 파라미터를 Robot클래스로 지정했다.
'Java' 카테고리의 다른 글
11.07.(월) Java-13: 재귀 함수 (0) | 2022.11.09 |
---|---|
10.26.(수) Java-12: 인터페이스 (0) | 2022.10.30 |
10.24.(월) Java-10: 클래스(2) (0) | 2022.10.26 |
10.21.(금) Java-09: 클래스(1) (0) | 2022.10.22 |
10.21.(금) Java-08: 함수 (0) | 2022.10.22 |