[자바의 정석] CHAPTER 7: 객체지향 프로그래밍 Ⅱ

1. 상속

상속이란?

  • 기존의 클래스를 재사용하여 새로운 클래스를 작성하는 것이다.

상속의 장점?

  • 적은 양의 코드로 새로운 클래스를 작성할 수 있다.
  • 코드를 공통적으로 관리할 수 있다.
    • 코드의 추가 및 변경이 용이하다.
    • 코드의 중복이 줄어든다. 중복이 많아지면 유지보수가 어려워지고 일관성을 유지하기 어렵다.

코드의 중복성을 최소화해야하는 이유?
프로그램이 오작동을 하는 이유 중에 하나는 중복된 코드 중에서 바르게 변경되지 않은 곳이 있기 때문이다.

Parent 클래스에 age라는 정수형 변수를 멤버변수로 추가하면, Child 클래스는 Parent클래스의 멤버를 모두 상속받는다.

  • 생성자와 초기화 블럭은 상속되지 않는다. 멤버만 상속된다.
  • 자손 클래스의 멤버 개수는 조상 클래스보다 항상 같거나 많다.
  • 자손 클래스 간에는 아묵런 관계도 성립되지 않는다.

클래스를 재사용하는 2가지 방법: 상속 vs 포함

  • 상속관계
    • ~은 ~이다
    • is - a
  • 포함관계
    • ~은 ~을 가지고 있다.
    • has - a
public class DrewShape {
    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(p[1], 50);

        t.draw();
        c.drew();
    }
}

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 + ")";
    }
}

class Circle extends Shape { //상속
    Point center; // 포함
    int r;

    Circle() {
        this(new Point(0, 0), 100);
    }

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

    void drew() {
        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);
    }
}

자바에서 단일상속만을 허용하는 이유?
두개의 부모 클래스에 동일한 메서드가 있다면, 자손 클래스는 어떤 부모 클래스의 메서드를 상속받아야 하는 것인지(static 메서드라면 메서드 이름 앞에 클래스의 이름을 붙여서 구별할 수 있지만, 인스턴스 메서드는 불가하다)하는 문제점을 해결한다.
또한, 클래스 간의 관계가 보다 명확해지고 코드를 더욱 신뢰할 수 있게 만들어준다는 장점도 있다.

2. 오버라이딩

오버라이딩인란?

  • 조상 클래스로부터 상속받은 메서드의 내용을 변경하는 것이다.

오버라이딩의 조건

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

  • 이름이 같아야 한다.
  • 매개변수가 같아야 한다.
  • 반환타입이 같아야 한다.
    • JDK1.5부터 '공변 반혼타입'이 추가되어, 반환타입을 자손 클래스의 타입으로 변경하는 것은 가능하도록 완화되었다.

😎선업부가 서로 일치해야 한다.

하지만 제한된 조건 하에서만 변경할 수 있는데

  • 접근 제어자는 조상 클래스의 메서드보다 좁은 범위로 변경할 수 없다.
    • public > protected > defulat > private
  • 조상 클래스의 메서드보다 많은 수의 예외를 선언할 수 없다.
  • 인스턴스메서드를 메서드로 또는 그 반대로 변경할 수 없다.

조상 클래스에 정의된 static 메서드를 자손 클래스에서 똑같은 이름의 static 메서드로 정의할 수 있을까?
가능하다. 하지만, 이것은 각 클래스에 별개의 static메서드를 정의한 것일 뿐이다. 오버라이딩은 아니다.

오버로딩 vs 오버라이딩

  • 오버로딩
    • 기존에 없는 새로운 메서드를 정의하는 것
  • 오버라이딩
    • 상속받은 메서드의 내용을 변경하는 것

super

  • 자손 클래스에서 조상 클래스로부터 상속받은 멤버를 참조하는데 사용되는 참조변수이다.
  • 상속받은 멤버와 자신의 멤버와 이름이 같을 때 super를 붙여서 구별할 수 있다.
  • static메서드는 인스턴스와 상관이 없으므로, this와 마찬가지로 static메서드에서는 사용할 수 없다.
  • 변수만이 아니라 메서드 역시 super를 써서 호출할 수 있다.

super() - 조상 클래스의 생성자

  • Object 클래스를 제외한 모든 클래스의 생성자 첫줄에 생성자.this() 또는 super()를 호출해야 한다. 그렇지 않으면 컴파일러가 자동적으로 'super();'를 생성자의 첫줄에 삽입한다.
public class PointTest2 {
    public static void main(String[] args) {
        Point3D p3 = new Point3D();
        System.out.println("p3.x = " + p3.x);
        System.out.println("p3.y = " + p3.y);
        System.out.println("p3.z = " + p3.z);
    }
}

class Point {
    int x = 10;
    int y = 20;

    Point(int x, int y) {
    //생성자 첫줄엥서 다른 생성자를 호출하지 않기 때문에 컴파일러가 'super();'를 여기에 삽입한다.
        this.x = x;
        this.y = y;
    }
}

class Point3D extends Point{
    int z = 30;

    Point3D() {
        this(100, 200, 300);
    }

    Point3D(int x, int y, int z) {
        super(x, y);
        this.z = z;
    }
}

위 코드의 생성자 호출순서를 정리해보자면,

Point3D -> Point3D(int x, int y, int z) -> Point(int x, int y) -> Object()

package와 import

패키지란?

  • 클래스의 묶음이다.
  • 같은 이름의 클래스일지라도 서로 다른 패키징에 존재하는 것이 가능하다.
    • 다른 개발자가 개발한 클래스 라이브러리의 클래스와 이름이 충돌하는 것을 피할 수 있다.
  • 클래스: 하나의 클래스파일, 패키지: 하나의 디렉토리

static import문

  • static 멤버를 호출할 때 클래스 이름을 생략할 수 있다.
  • 코드가 간결해진다.
System.out.println(Math.random())

static import문을 선언했다면,

import static java.lang.Integer.*;
import static java.lang.Math.random;
import static java.lang.System.out;

out.println(random())

좋은 웹페이지 즐겨찾기