🙈

⃝ 동글동글 ⃝

🪐ᐩ˖ 🍎

Java/기초

[Java 기초] 상속(Inheritance)과 오버라이딩(Overriding)

JONG_UK 2023. 5. 12. 16:39
728x90
반응형
SMALL

1. 상속 (Inheritance)

1) 상속의 정의와 장점

정의 : 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것

- 두 클래스를 조상과 자손으로 관계를 맺어주는 것.

- 자손은 조상의 모든 멤버를 상속받는다.(생성자, 초기화블록 제외)

- 자손의 멤버 개수는 조상보다 적을 수 없다.(같거나 많다.)

 

장점 : 코드의 재사용성을 높이고 코드의 중복을 제거하여 프로그램의 생산성과 유지보수에 기여

 

상속 관계에는 조상 클래스와 자손 클래스로 두 가지로 나뉘는데 조상 클래스와 자손 클래스는 아래와 같은 이름을 가진다.

- 조상 클래스 : 부모(parent) 클래스, 상위(super) 클래스, 기반(base) 클래스

- 자손 클래스 : 자식(child) 클래스, 하위(sub) 클래스, 파생된(derived) 클래스

 

예시
class Tv { // 조상 클래스, 부모 클래스
    boolean power;
    int channel;
    
    void power() { power = !power; }
    void channelUp() { ++channel; }
    void channelDown() { --channel; }
}

class CaptionTv extends Tv { // 자손 클래스, 자식 클래스
    boolean caption;
    
    void displayCaption(String text) {
        if (caption) { System.out.println(text); }
    }
}

public class CaptionTvTest { // Main 클래스
    public static void main(String[] args) {
        CaptionTv ctv = new CaptionTv();
        
        ctv.channel = 10;
        ctv.channelUp();
        System.out.println(ctv.channel);
        
        ctv.displayCaption("Hello, World");
        
        ctv.caption = true;
        ctv.displayCaption("Hello, World");
    }
}

 

CaptionTv 클래스는 Tv 클래스를 상속받아 작성되었고, CapthonTvTest 클래스에서 CaptionTv 인스턴스를 생성하였다.

ctv 인스턴스를 이용해서 ctv 인스턴스(자식 클래스)가 부모 클래스인 Tv 클래스 메서드를 호출하게 된다.

 

2) 상속 관계의 특징

- Java는 단일 상속만을 허용한다.

- 자손 클래스는 조상 클래스의 멤버를 모두 상속받는다.

- 조상 클래스가 변경되면 자손 클래스에도 영향을 미친다. 하지만 자손 클래스가 변경되면 조상 클래스에 영향을 미치지 않는다.

- 자식(자손) 클래스들의 공통부분은 조상 클래스에서 관리하는 게 좋다.

 

 

Java가 단일 상속만을 허용하는 이유 

위 그림을 봤을 때 Child 클래스와 Child2 클래스에 동일한 이름의 family() 메서드가 정의되어 있고, 두 클래스를 상속받는 GrandChild 클래스가 생성된다면, 어떤 메서드가 실행되어야 할지 모르게 된다. 메서드를 식별하는데 문제가 생기기 때문에 단일 상속만을 허용하고, 단일 상속을 하게 된다면 클래스 간의 관계가 보다 명확해지고 코드를 더욱 신뢰할 수 있게 만들어 준다.

 

3) 포함 관계

상속 관계와는 다른 포함 관계

포함 관계는 자식 클래스에 부모 클래스를 상속받는 것이 아닌, 클래스 내부에 다른 클래스를 멤버변수(인스턴스)로 생성하는 것이다.

 

 

4) 상속 관계와 포함 관계 쉽게 이해하기

class Point {
	int x;
	int y;
}

class Circle {
	int x, y, z;
}

상속 관계를 해야 할지, 포함 관계를 사용해야 할지 헷갈리다면

        상속관계 : '~은 ~이다(is-a)'와

        포함 관계 : '~은 ~을 가지고 있다(has-a)'

을 넣어서 문장을 만들어 보면 명확해진다.

 

상속 관계 : 원(Circle)은 점(Point)이다. -> Circle is a Point

포함 관계 : 원(Circle)은 점(Point)을 가지고 있다. -> Circle has a Point

// 상속 관계
class Circle extends Point{
	int r;
}

// 포함 관계
class Circle {
	Point c = new Point();
	int r;
}

두 예시를 보면 '원은 점이다'라는 것이 더 명확하기 때문에 상속 관계로 정의해 주면 된다.

 

예시
class DrawShape {
    public static void main(String[] args) {
        Point[] p = {   new Point(100, 100),
                new Point(140,  50),
                new Point(200, 100)
        };

        Triangle t = new Triangle(p);
        Circle   c = new Circle(new Point(150, 150), 50);

        t.draw(); // 삼각형을 그린다.
        c.draw(); // 원을 그린다.
    }
}

class Shape {
    String color = "black";
    void draw() {
        System.out.printf("[color=%s]%n", color);
    }
}

class Point {
    int x;
    int y;

    Point(int x, int y) {
        this.x = x;
        this.y = y;
    }

    Point() {
        this(0,0);
    }

    String getXY() {
        return "("+x+","+y+")"; // x와 y의 값을 문자열로 반환
    }
}

class Circle extends Shape {
    Point center;  // 원의 원점좌표
    int r;       // 반지름

    Circle() {
        this(new Point(0, 0), 100); // Circle(Point center, int r)를 호출
    }

    Circle(Point center, int r) {
        this.center = center;
        this.r = r;
    }

    void draw() { // 원을 그리는 대신에 원의 정보를 출력하도록 했다.
        System.out.printf(
            "[center=(%d, %d), r=%d, color=%s]%n",
                center.x, center.y, r, color
        );
    }
}

class Triangle extends Shape {
    Point[] p = new Point[3];

    Triangle(Point[] p) {
        this.p = p;
    }

    void draw() {
        System.out.printf(
            "[p1=%s, p2=%s, p3=%s, color=%s]%n",
                p[0].getXY(), p[1].getXY(), p[2].getXY(), color
        );
    }
}

 

 

5) 모든 클래스의 조상 Object 클래스

- Object 클래스는 모든 클래스 상속계층도의 최상위에 존재하는 조상 클래스이다.

- 다른 클래스로부터 상속받지 않는 모든 클래스들(ex. Tv클래스) 

- Object 클래스에는 toString(), equals() 같은 모든 인스턴스가 가져야 할 기본적인 11개의 메서드가 정의되어 있다.

- Object 클래스는 코드를 컴파일하면 컴파일러가 자동적으로 'extends Object' 클래스를 추가해 준다.

class Tv extends Object {
    boolean power;
    int channel;
    
    void power() { power = !power; }
    void channelUp() { ++channel; }
    void channelDown() { --channel; }
}

- 이미 어떤 클래스로부터 상속받도록 작성된 클래스에 대해서는 컴파일러가 'extends Object'를 추가하지 않는다.

 

2. 오버라이딩 (Overriding)

1) 오버라이딩이란?

정의 : 조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것

- 부모 클래스로부터 상속받은 메서드를 그대로 사용하거나, 자손 클래스 자신에 맞게 변경해야 하는 경우 사용

 

조건 : 자손 클래스에서 오버라이딩하는 메서드는 조상 클래스의 메서드와

- 이름이 같아야 한다.

- 매개변수가 같아야 한다.

- 반환타입이 같아야 한다.

오버라이딩은 메서드의 내용만을 새로 작성하는 것이기 때문에, 메서드의 선언부는 조상의 것과 완전히 일치해야 한다.

 

2) 오버로딩(overloading) vs 오버라이딩(overriding)

오버로딩은 기존에 없는 메서드를 추가하는 것 - new

오버라이딩은 조상으로부터 상속받은 메서드를 자손 클래스에서 재정의 하는 것 - change, modify

class Parent {
	void parentMethod() {}
}

class Child extends Parent {
	void parentMethod() {} 	// 오버라이딩
	void parentMethod(int n) {} // 오버로딩
    
	void childMethod() {} 	// 오버라이딩
	void childMethod(int n) {}  // 오버로딩
}

 

3) super

- super는 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는 데 사용하는 참조변수

- 조상 클래스의 멤버와 자손 클래스의 멤버가 중복 정의되어 서로 구별해야하는 경우에만 사용하는 것이 좋다.

- 인스턴스 메서드에서 자신이 속한 인스턴스의 주소가 지역 변수에 저장되는데, 이것이 참조변수인 this와 super의 값이 된다.

 

예시
class PointTest {
	public static void main(String args[]) {
		Point3D p3 = new Point3D(1,2,3);
	}
}

class Point {
	int x;	
	int y;

	Point(int x, int y) {
		this.x = x;
		this.y = y;
	}

	String getLocation() {
		return "x :" + x + ", y :"+ y;
	}
}

class Point3D extends Point {
	int z;

	Point3D(int x, int y, int z) {
    	// super(x, y); // 생략되어 있는 super()메소드
		this.x = x;
		this.y = y;
		this.z = z;
	}

	String getLocation() {	// 오버라이딩
		return super.getLocation() + ", z :" + z; // 조상의 메서드 호출
	}	
}

 

4) this vs super

this 와 super는 비슷하지만 조금 다르다. 

아래 예제에서 this는 Parent 부모 클래스의 x를 가리키고

class SuperTest {
	public static void main(String args[]) {
		Child c = new Child();
		c.method();
	}
}

class Parent {
	int x=10;
}

class Child extends Parent {
	void method() {
		System.out.println("x=" + x);
		System.out.println("this.x=" + this.x);
		System.out.println("super.x="+ super.x);
	}
}

 

아래 예제에서는 this.x는 Child 자식 클래스의 x를 가리킨다.

또한 여기서의 super.x는 Parent 부모 클래스의 x를 가리킨다.

class SuperTest2 {
	public static void main(String args[]) {
		Child c = new Child();
		c.method();
	}
}
class Parent {
	int x=10;
}

class Child extends Parent {
	int x=20;

	void method() {
		System.out.println("x=" + x);
		System.out.println("this.x=" + this.x);
		System.out.println("super.x="+ super.x);
	}
}

둘의 차이는 부모 클래스와 자식 클래스에 똑같은 변수가 있는지 없는지의 차이이다. 

따라서 this.x는 자식 클래스에서 선언된 변수 x가 부모 클래스에 없다면 자식 클래스의 x를 출력하게 되며,

부모 클래스의 x는 super.x를 통해 출력하게 된다.

 

Object 클래스를 제외한 모든 클래스의 생성자 첫 줄에는 생성자 this() 또는 super()를 호출해야 한다. 그렇지 않으면 컴파일러가 자동적으로 'super();' 메서드를 생성자의 첫 줄에 삽입한다.

 

728x90
반응형
LIST

'Java > 기초' 카테고리의 다른 글

[Java 기초] Random 클래스  (0) 2023.05.20
[Java 기초] Objects 클래스  (0) 2023.05.19
[Java 기초] Wrapper(래퍼) 클래스  (1) 2023.05.19
[Java 기초] Scanner(스캐너)에 대하여  (0) 2023.05.19
Java 문자열(String) 관련 함수  (2) 2023.01.02