본문 바로가기
프로그래밍/java

8. java 상속, 오버라이딩, getter&setter

by 수삼이는코딩중 2023. 4. 10.
728x90

상속

oo는oo다

상속관계는 is a관계 또는 kind of 관계 라고 말하기도 한다.

상속 = 일반화 + 확장

  • 버스, 레미콘, 트럭을 자동차라고 부른다면 그것은 일반화.
  • 자동차 + 삽 = 포크레인 이라고 하면 확장! 자동차를 확장하는 개념.

부모와 자식관계

  • 부모쪽에 화살표 표시를 해준다.
  • 부모의 기능을 자식도 가진다. 
  • 일반화 시킨다 : 자식클래스들을 부모클래스로 부를 수 있는 것을 말한다. (TV는 전자제품이다)

상속은 가장 강한 결합이다. 반드시 써야할 때만 쓰고 되도록 사용하지 않는 게 좋다. (상속은 배우지만 좋지 않다!)

좋은 객체는 응집도는 높고 결합도는 낮아야 하기 때문!

잘못 상속 받으면 타격이 너무 크다. 

 

상속 선언 방법

[접근제한자][abstract final] class 클래스명 extends 부모클래스명 {
	...
    }

아무것도 상속 받지 않으면 자동으로 java.lang.Object를 상속받는다. 즉 모든 클래스는 Object의 자손!

 

Car car = new Bus();

이때까지 배웠던 것을 참고하면, 원래 Car과 Bus()가 같아야 하는데 여기서는 상속을 받았기 때문에

Bus가 Car의 자식이면 성립이 되는 문장이다. 만들어진 인스턴스는 Bus인데 그 Bus를 Car, 즉 자동차기능으로만 사용할 거다.

왜 이렇게 쓸까?

Bus b1 = new Bus();

위와 같이 쓰면 Car에 담긴 기능을 쓸 수 없다. b1의 타입은 Bus.

 

Bus에 있는 기능 말고, Car에 있는 기능만쓰고 싶을 때는 다음과 같이 써줘야 한다.

Car c1 = new Bus();

c1은 Car의 기능만 이용할 수 있다. 

 

근데 Car로 쓸건데 왜 Bus의 인스턴스를 만들까?

Bus를 배달해주는 사람 입장에서는 Bus에 들어있는 특정 메소드를 건드릴 필요가 없으니 Car의 기능, 즉, 달리는 기능만 이용하면 되기 때문에 저렇게 사용한다. 발렛파킹도 마찬가지.

즉, type만 보면 어떤 메소드만 사용할 수 있는지 안다.

 

다형성 - 메소드 오버라이딩(Overriding)

  • over+ride = 올라타다
  • 상위 클래스의 메소드를 하위 클래스가 재정의 하는 것
  • 메소드의 이름, 파라미터 갯수, 타입도 동일 해야 하며, 주로 상위 클래스의 동작을 상속 받은 하위 클래스에서 변경하기 위해 사용된다. 

 

메소드가 오버라이딩 되면 무조건 자식의 메소드가 실행된다.

예를 들어 

부모의 run()메소드는 "전륜구동으로 달린다"이지만

부모를 상속받은 자식들, super car 또는 Bus가 "사륜구동으로 달린다"로 변경하고 싶을 때. 

변경하고 싶은 메소드를, 자식 클래스에서 그대로 써준다 (파라미터 타입, 갯수를 동일하게! 아니면 overloading이 된다)

 

Car클래스 생성

public class Car {
    public void run(){
        System.out.println("전륜구동으로 달리다");
    }
}

Car클래스를 상속받는 Bus클래스 생성

단, run()메소드를 오버라이딩하여 변경하였다. 

public class Bus extends Car {
    public void run(){
        System.out.println("후륜구동으로 달리다");
    }
    public void 안내방송(){
        System.out.println("안내방송하다");
    }
}

메인메소드 생성

public class CarExam01 {
    public static void main(String[] args){
        Bus b1 = new Bus();
        b1.run();
        b1.안내방송();

        Car c1 = new Bus(); // 버스는 자동차다.
        c1.run();
        //c1.안내방송();안된다
    }
}

c1 과 b1 모두 Bus 인스턴스를 만들었다. 

그래서 Bus는 후륜으로 달리도록 만들어 놨는데 갑자기 전륜으로 바뀌지 않는다. (run()이라는 메소드는 Car과 Bus에 둘다 선언되어있다.) 따라서 Bus 인스턴스를 만들면 오버라이딩 한 메소드는 무조건 자식 메소드가 실행된다. 

 

결과

후륜구동으로 달리다
안내방송하다
후륜구동으로 달리다

(사실 프로그램 자체가 만들어진 목적이 있기 때문에 원래 이렇게 돼야...)

 

Bus인스턴스를 참조하는 Car타입 c1참조변수가 참조하는 Bus의 인스턴스를 참조하는 Bus타입 b2생성ㅋㅋ

        Car c1 = new Bus(); // 버스는 자동차다.
        c1.run();

        Bus b2 = (Bus)c1;
        b2.안내방송();

b2의 타입은 Bus이기 때문에 Bus의 필드나 메소드를 사용할 수 있다. 

원래 c1이 가리키던게 Bus인스턴스였기 때문에 가능한 일.

단 버스를 가리키면서 자동차라고 할 수는 있지만 자동차를 가리키면서 저것은 버스다 라고 할 수는 없다. 

 

필드는 Type을 따라가고 메소드는 무조건 오버라이딩된 자식의 메소드가 실행된다. 

public class Exam01 {
    public static void main(String[] args){
        Parent p1 = new Parent();
        System.out.println(p1.i);
        p1.printI();

        Child c1 = new Child();
        System.out.println(c1.i);
        c1.printI();

        Parent p2 = new Child(); //Child 는 Parent의 자식.
        System.out.println(p2.i);
        p2.printI();
    }
}

결과

5
Parent - printI(): 5
15
child - printI() : 15
5
child - printI() : 15

Parent 타입인 p2는 Child 인스턴스를 참조하고 있다.

그래서 filed인 i 값은 Parent 타입을 따라가고 있고, 메소드인 printI()는 Child에 오버라이딩된 값을 따라간다. 

 

메소드 오버라이딩만 기억한다.

 

왜 이런식으로 작동이 되느냐?

필드는 은닉해야 한다. 외부에서 필드를 직접 접근하지 못하도록 해야한다는 의미. 접근제한자 사용.

필드에 직접 접근하는 코드는 좋지 않다. 메소드를 통해서만 하도록 해야 한다. 

 

Parent 클래스에 아래의 메소드를 추가하면

    public void printII(){
        System.out.println(i*2); //10
    }

다음과 같은 결과가 나온다. (Child는 위 메소드를 오버라이딩하지 않았다.)

 

        Parent p2 = new Child(); //Child 는 Parent의 자식.
        System.out.println(p2.i);
        p2.printI();
        p2.printII();

결과

5
child - printI() : 15
10

즉, 필드값은 오버라이딩에 영향을 받지 않는다.

클래스를 생성할 때 내가 만들어놓은 필드는 변하지 않아야 한다 

 

getter,setter메소드

Book class 생성

public class Book {
    public int price;

}

BookExam class 생성

public class BookExam01 {
    public static void main(String[] args) {
        Book b1 = new Book();
        b1.price = 100;
        System.out.println(b1.price);
    }
}

결과

100

그러나, 필드에 직접 접근하는 것은 좋지 않다.

보통 필드는 private으로 제한 -> 직접접근하지 못하게됨 -> 필드를 접근하는 메소드를 만든다 (필드의 값을 수정하고 얻기위한 메소드. setter, getter. 단축기 : alt+insert)

 

다음과 같은 setter getter 메소드를 Book class에 선언해준다. 

    public int getPrice() {
        return this.price; // this는 내 자신 인스턴스를 참조하는 예약어. static메소드에서 사용 안됨.
    }

    public void setPrice(int price) { //지역변수 price
        this.price = price;
    }

다음과 같이 수정한다.

public class BookExam01 {
    public static void main(String[] args) {
        Book b1 = new Book();
       // b1.price = 100;
       // System.out.println(b1.price);

        b1.setPrice(500);
        System.out.println(b1.getPrice());
    }
}

결과

500

필드에 접근하여 값을 넣었다. 

 

    public int getPrice() {
        return this.price*2; // this는 내 자신 인스턴스를 참조하는 예약어. static메소드에서 사용 안됨.
    }

getter메소드를 위처럼 수정하면 결과는 1000이 나온다. 

즉 getter 메소드를 이용해 값을 가공해 반환할 수 있다. 

 

setter, getter - 프로퍼티(property)

위 코드에서는 price 프로퍼티

(필드 Price는 클래스가 가지는 것)

 

    private String title;
    
    //setter, getter생성
    public String getName() {
        return title;
    }

    public void setName(String title) {
        this.title = title;
    }
        b1.setName("수삼이의 즐거운 자바");
        System.out.println(b1.getName());

결과

수삼이의 즐거운 자바

Name 프로퍼티 생성

즉, 필드에서 변수명은 Title이지만, 프로퍼티는 Name

 

Object가 오버라이딩하라고 제공하는 메소드

toString()
equals()
hashCode()

부모타입의 변수로 자식 인스턴스를 참조할 수 있다.

조상타입의 변수로 후손인스턴스를 참조할 수 있다.

아무것도 상속받지 않으면 Object를 상속받는다. 

Object o1 = new Car();
Object o2 = new Bus();
Object o3 = new 이층버스();

따라서 위와 같은 코드가 가능해진다. 

public class CarExam2 {
    public static void main(String[] args){
        Car c1 = new Car();
        System.out.println(c1); //println(Object x), Object로 참조할 수 있는 것은 무엇이든 받을 수 있다.
    }
}

실제로 println(c1)은 println(c1.toString())과 같은 결과다. println(Object x) 메소드가 그렇다!

근데 사실 결과값이 필요없는 값이다

따라서 c1을 출력하고 싶으면 car class에 toString() 메소드를 오버라이딩 해준다.

alt + insert -> override

    @Override
    public String toString() {
        return "자동차";
    }

이렇게해주면

public class CarExam2 {
    public static void main(String[] args){
        Car c1 = new Car();
        System.out.println(c1); //println(Object x)
    }
}

결과

자동차

 

'프로그래밍 > java' 카테고리의 다른 글

11. java 템플릿 메소드 패턴  (0) 2023.05.23
9. java 생성자  (0) 2023.05.11
7. java 패키지  (0) 2023.04.10
6. java 클래스 잘 만들기  (0) 2023.04.10
5. java 필드, static field  (0) 2023.04.05

댓글