본문 바로가기

프로그래밍/자바

[자바/기본] 상속 Inheritance

728x90
반응형
SMALL

상속이 없다면

  • 상속이 없다면 서로 연관성이 있는 INSTANCE 두개를 선언해 주어야한다. 이는 코드의 재사용성이 전혀 없는 코드가 된다.
  • 예제
public class Inherit01Main {

    public static void main(String[] args) {
       BasicTV tv1 = new BasicTV();
       tv1.isPowerOn = true;
       tv1.volume = 10;
       tv1.channel = 5;
       tv1.displayInfo();

       SmartTV tv2 = new SmartTV();
       tv2.isPowerOn = true;
       tv2.channel = 123;
       tv2.volume = 12;
       tv2.ip = "192.168.0.111";
       tv2.displayInfo();


       System.out.println("\n프로그램 종료");
    } // end main()
} // end class

 - 위 예시를 보면 BasicTV 클래스와 SmartTV 는 함께 공유 될 수 있는 멤버 변수가 있고 연관성도 있지만 상속이 없다면 이러한 코드가 작성이 될 것이다.


상속

  • 상속이란 상위클래스에서 하위클래스를 정의 하는 방법
  • [class] 하위 클래스 extends 상위클래스 + 자바에서는 하나의 클래스만 상속받을 수 있음
  • 상속의 이점은 기존의 객체를 그래도 사용하면서 새로운 객체에서 추가 및 변경 되는 부분만 작성함으로 소프트웨어 개발 효율을 높일 수 있다.
  • 예제
public class BasicTV {
    // 멤버 변수
    boolean isPowerOn;
    int channel;
    int volume;
    
    // 메소드
    public void displayInfo() {
       System.out.println("--- TV 현재 상태 ---");
       System.out.println("전원: " + isPowerOn);
       System.out.println("채널: " + channel);
       System.out.println("볼륨: " + volume);
    } // end displayInfo()
    
} // class BasicTV


public class SmartTV extends BasicTV{

    String ip;

    public void displayInfo(){
        super.displayInfo(); // 부모(super) 의 displayInfo() 를 먼저 실행하고
        System.out.println("IP 주소: " + ip);
    }

}


// 구문
		BasicTV tv1 = new BasicTV();
		tv1.displayInfo();
		
		System.out.println();

		// SmartTV 클래스의 인스턴스 생성
		SmartTV tv2 = new SmartTV();

		tv2.isPowerOn = true;
		tv2.channel = 123;
		tv2.volume = 12;
		tv2.ip = "192.169.0.110";

		tv2.displayInfo();

 - 실제 돌아가는 코드는 아님.

 - displayInfo() 함수는 BasicTV에만 있었으나 SmartTV 가 BasicTV클래스로부터 상속 받고 있기 때문에 함수 사용 가능


상속과 생성자

  • 자식 클래스의 생성자에서 명시적으로 부모클래스가 호출되어있지않으면 자동으로 부모클래스의 디폴트 생성자가 호출
  • 이러한 이유로 자식 클래스의 생성자에서 명시적으로 부모 클래스의 생성자를 호출 하기도 함
    • super([paramter]) 키워드를 사용하여 부모 클래스의 생성자를 호출
    • super 함수는 항상 제일 처음에 호출 되어야함
    • 부모 클래스에 디폴트 생성자가 없을 경우, 다른 생성자를 반드시 명시적으로 호출해줘야한다.
  • 어떤 경우에 상속으로 객체를 설계 해야하는지 HONEY TIP
    • HAS - A 관계 : 클래스 멤버로 설계
      • car tire 가 있다면
      • car has a tire
    • IS - A 관계 : 상속으로 설계
      • vehicle, car, hybrid car 가 있다면
        • car is a vehicle
        • hybrid car is a car
  • 예제
public class Vehicle {
    int speed;
    
    public Vehicle(){
        System.out.println("Vehicle() 생성");
    }

    public Vehicle(int speed) {
        this.speed = speed;
        System.out.println("Vehicle(int) 생성: speed = " + speed);
    }
}

public class Car extends Vehicle{
    int oil;
    
    public Car(){
        System.out.println("Car() 생성");
    }

    public Car(int oil){
        // 명시적으로 부모 생성자 호출
        super(54);
        this.oil = oil;
        System.out.println("Car(int) 생성 : oil = " + oil);
    }

    public Car(int speed, int oil){
        super(speed);
        this.oil = oil;
        System.out.println("Car(int,int) 생성: speed=" + speed
                + "oil=" + oil);
    }
}

public class HybridCar extends Car {
    int electricity;
    
    public HybridCar(){
        System.out.println("HybridCar() 생성");
    }
}


		// Vehicle 클래스의 인스턴스 생성
		Vehicle v1 = new Vehicle();
		Vehicle v2 = new Vehicle(100);
		
		System.out.println();
		// Car 클래스의 인스턴스 생성
		Car car1 = new Car();
		System.out.println("speed: " + car1.speed);
		System.out.println("oil: " + car1.oil);

		Car car2 = new Car(400);
		
		System.out.println();
		// HybridCar 클래스의 인스턴스 생성
		HybridCar car3 = new HybridCar();

상속과 접근 권한

  • private : 자기 클래스에서만
  • default : 자기 클래스 + 같은 패키지 안에서
  • protected : 자기 클래스 + 같은 패키지 + 상속 관계 일 때
  • public : 어디에서나
  • 저번에 포스팅 했던 Modifier 참고하면 이해가 쉬울 것 같습니다.
  • private < default < protected < public 을 순서로 더 큰 범위를 커버가 가능하고 이를 지키지 않고 상속했을 시 컴파일 에러가 나올 수 있습니다!

상속에서 메소드 재정의 (Overriding)

  • 상속 관계에서 부모 클래스에 있던 메소드를 재정의 하는 것
  • 부모 클래스에 있는 메소드와 매개변수 리스트가 동일 해야함
  • 부모 클래스에 있는 메소드의 접근 권한 수식어가 동일할 필요는 없지만 범위를 축소시켜서는 안됨
  • final : 더 이상의 오버라이딩 및 상속 불가 하게 만드는 Modifier
  • 예제
public class Person {
    // 멤버 변수
    private String name;
    // getter & setter
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    // 메소드
    protected void showInfo() {
        System.out.println("이름: " + name);
    }
    public final void whoAreYou(){
        System.out.println("이름: " + name);
    }
}


public class BusinessPerson extends Person {

    private String company;

    // getter & setter
    public String getCompany() {
        return company;
    }
    public void setCompany(String company) {
        this.company = company;
    }
//    @Override
//    protected void showInfo(){
//        super.showInfo();
//        System.out.println("회사: " + company);
//    }
    @Override
//    void showInfo() {  // (불가)
//    private showInfo() {  // (불가)
//    protected void showInfo() {  // (가능)
     public void showInfo() {  // (쌉가능)
        super.showInfo();
        System.out.println("회사: " + company);
    }
    public void showInfo(int id){  // 오버로딩!

    }
    @Override
    public String toString() {
        return String.format("[BusinessPerson: name=%s, company=%s]", getName(), company);
    }
//    @Override
//    public void whoAreYou() {
//        super.whoAreYou();
//    }
}

		Person p1 = new Person();
		p1.setName("abc");
		p1.showInfo();
		
		System.out.println();
		// BusinessPerson 클래스의 인스턴스를 생성
		BusinessPerson p2 = new BusinessPerson();
		p2.setName("이몽룡");
		p2.setCompany("(주)조선");
		p2.showInfo();  // BusinessPerson 에서 overriding 한 메소드 실행

		System.out.println(p2);  // p2.toString()

위 예제를 통해 Modifier (접근 제어자)가 더 좁은 범위에 있는 것들을 주석 처리 하였다 (이유는 컴파일 에러)

 

위 코드를 실행 시키면 Person 일 때, BussinessPerson 일 때, 같은 메소드를 사용하지만 다른 결과값을 가질 수 있다.

 

728x90
반응형
LIST