포스트

Day26 Java프로그래밍 기초(상속)

  1. 상속

1.1 상속의 개념

  • 부모클래스를 상속받은 자식클래스에서 부모클래스의 필드,메소드를 활용 하는 방법을 말한다.

  • 부모클래스의 멤버를 자식클래스에 복사하는것이 아니라, 부모클래스의 멤버를 사용하는 것일 뿐이다.

  • 직접적으로 바이트 코드를 복사하지 않는다.

1.2 클래스 상속

  • 클래스 상속 extends를 사용하여 자식클래스가 부모클래스로 부터 상속 받았다는것을 표기한다.

    public class Parent{ 필드 메소드() }

    public class Child extends Parent{ 필드 메소드() }

1.3 부모 생성자 호출

  • 클래스 상속을 받은 자식클래스를 인스턴스 하면 부모클래스의 생성자가 자동적(super())으로 호출된다.

    public class SmartPhone extends Phone { //자식 생성자 선언 public SmartPhone(String model, String color) { super(); this.model = model; this.color = color; System.out.println(“SmartPhone(String model, String color) 생성자 실행됨”); } }

  • 부모클래스의 생성자가 매개변수를 가지고 있다면, 자식클래스도 super(매개변수)의 구조로 사용해야한다.

1.4 메서드 오버라이딩

  • 자식클래스에서 부모클래스의 메서드를 재정의 하는 방법이다.
    • 부모 메서드의 선언부(리턴 타입, 메서드명, 매개변수)는 동일해야한다.
    • 접근제한을 더 강하게 오버라이딩 할수 없다.
    • 오버라이딩시 어노테이션(@override)를 사용하면 컴파일 오류를 막을 수 있다.

    public class Computer extends Calculator { //메소드 오버라이딩 @Override public double areaCircle(double r) { System.out.println(“Computer 객체의 arearCircle() 실행”); return Math.PI * r * r; } }

1.5 부모메서드 호출

  • 자식클래스에서 부모클래스의 메서드를 호출 시에는 super.부모메서드명()을 사용한다.

    public class SupersonicAirplane extends Airplane { //상수 선언 public static final int NORMAL = 1; public static final int SUPERSONIC = 2; //상태 필드 선언 public int flyMode = NORMAL;

    //메소드 재정의 @Override public void fly() { if(flyMode == SUPERSONIC) { System.out.println(“초음속비행합니다.”); } else { //Airpanle 객체의 fly() 메소드 호출 super.fly(); } }

    }

1.6 타입변환

  • 자동 타입변환 : 부모클래스 타입의 변수가 자식 타입의 객체를 사용하면 자동 타입변환이 이 일어난다.

  • 부모클래스에 선언된 필드와 메서드만 접근이 가능하다.
  • 자식클래스에 오버라이딩 된 메서드가 있다면 오버라이딩 메소드가 실행된다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package ch07.sec07.exam02;

public class ChildExample {
  public static class Parent {
    // 메소드 선언
    public void method1() {
      System.out.println("Parent-method1()");
    }

    // 메소드 선언
    public void method2() {
      System.out.println("Parent-method2()");
    }
  }

  public static class Child extends Parent {
    // 메소드 오버라이딩
    @Override
    public void method2() {
      System.out.println("Child-method2()");
    }

    // 메소드 선언
    public void method3() {
      System.out.println("Child-method3()");
    }
  }

  public static void main(String[] args) {
    // 자식 객체 생성
    // Child child = new Child();

    // 자동 타입 변환
    Parent parent = new Child();

    // 메소드 호출
    parent.method1(); //Parent-method1()
    parent.method2(); // child-method2()
    // parent.method3(); (호출 불가능)
  }
}
  • 강제 타입변환 : 자식클래스 타입의 변수가 부모클래스의 타입의 변수로 생성되면 강제(캐스팅)을 해야한다.

    package ch07.sec07.exam03;

    public class ChildExample { public static class Parent { // 필드 선언 public String field1;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    
      // 메소드 선언
      public void method1() {
        System.out.println("Parent-method1()");
      }
    
      // 메소드 선언
      public void method2() {
        System.out.println("Parent-method2()");
      }
    }
    
    public static class Child extends Parent {
      // 필드 선언
      public String field2;
    
      // 메소드 선언
      public void method3() {
        System.out.println("Child-method3()");
      }
    }
    
    public static void main(String[] args) {
      // 객체 생성 및 자동 타입 변환
      Parent parent = new Child();
    
      // Parent 타입으로 필드와 메소드 사용
      parent.field1 = "data1";
      parent.method1();
      parent.method2();
      /*
       * parent.field2 = "data2"; //(불가능) parent.method3(); //(불가능)
       */
    
      // 강제 타입 변환
      Child child = (Child) parent;
    
      // Child 타입으로 필드와 메소드 사용
      child.field2 = "data2"; // (가능)
      child.method3(); // (가능)
    }   }
    

1.7 다형성

  • 자동 타입변환과 메서드 오버라이딩으로 인해 다형성이 가능해진다.

  • 다형성이란 같은 메서드선어부로 다른 결과를 만들수 있는 성질을 말한다.
    • 타이어 굴린다()의 메서드를 상속 받은 한국타이어,금호타이어가 있다고 가정하자.
    • 타이어 : 굴린다() = 타이어가 굴러간다.
    • 한국타이어 : 굴린다() = 한국 타이어가 굴러간다.
    • 금호타이어 : 굴린다() = 금호 타이어가 굴러간다.
    • 타이어 t1 = new 타이어 -> t1.굴린다() = 타이어가 굴러간다.
    • 타이어 t1 = new 한국타이어 -> t1.굴린다() = 한국타이어가 굴러간다.
    • 타이어 t1 = new 금호타이어 -> t1.굴린다() = 금호타이어가 굴러간다.
  • 이러한 성질을 사용하면, 매개변수의 다형성을 만들 수 있다.

    public class Driver { //메소드 선언(클래스 타입의 매개변수를 가지고 있음) public void drive(Vehicle vehicle) { vehicle.run(); } }

    public class Vehicle { //메소드 선언 public void run() { System.out.println(“차량이 달립니다.”); } }

    public class Bus extends Vehicle { //메소드 재정의(오버라이딩) @Override public void run() { System.out.println(“버스가 달립니다.”); } }

  • 자동 타입 변환 : Vehicle vehicle = new Bus();
    • 메서드 오버라이딩 : vehicle.run() - > bus.run()

    public class DriverExample { public static void main(String[] args) { //Driver 객체 생성 Driver driver = new Driver();

    //매개값으로 Bus 객체를 제공하고 driver 메소드 호출 Bus bus = new Bus(); driver.drive(bus); //driver.drive(new Bus()); 와 동일 }

    }

  • 이것을 응용하면 instanceof와 강제 형변환을 통해 부모클래스의 변수를 자식 클래스의 멤버를 사용할 수 있게 한다.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    
    public static void main(String[] args) {
      // 수퍼 클래스의 레퍼런스는 하위 클래스의 인스턴스를 담을 수 있다.
      Vehicle[] arr = new Vehicle[] {new Truck("기아자동차", 5, 10000, 32, 15f, true)};
      printCar(arr[0]);
    }
    // Vehicle v = new Truck("기아자동차", 5, 10000, 32, 15f, true);
    // 부모타입의 변수(v)로 자식타입(Truck)을 선언
      
    static void printCar(Vehicle v) {
      System.out.printf("모델: %s\n", v.model);
      System.out.printf("수용인원: %s\n", v.capacity);
      	// v는 car의 부모클래스 
      // car 가능
      if (v instanceof Car) {
        Car c = (Car) v;
        System.out.printf("cc: %s\n", c.cc);
        System.out.printf("밸브: %s\n", c.valve);
    
        if (v instanceof Sedan) {
          Sedan s = (Sedan) v;
          System.out.printf("썬루프: %b\n", s.sunroof);
          System.out.printf("오토: %b\n", s.auto);
        } else if (v instanceof Truck) {
        	//강제 형변환 
          Truck t = (Truck) v; 
            
          //오버라이딩 메서드 실행
          System.out.printf("톤: %f\n", t.ton);
          System.out.printf("덤프여부: %b\n", t.dump);
        }
      } else if (v instanceof Bike) {
        Bike b = (Bike) v;
        System.out.printf("engine: %b\n", b.engine);
      }
      System.out.println("-----------------------");
    }
    

    }

1.8 추상클래스

  • 추상메서드는 클래스들간에 공통적인 멤버를 선언하되 부모클래스를 직접적으로 사용할 수 없는 클래스이다.
    • 추상클래스 이기 때문에 new연산자를 통해 직접적인 생성을 불가능하다.
    • 자식클래스에서 상속을 받아 사용 해야한다.
    • 상속 받은 클래스에서는 필드, 메서드 생성자를 사용할 수 있다.

    // 추상 클래스 선언 public abstract class Phone { //필드 선언 String owner;

    //생성자 선언 Phone(String owner) { this.owner = owner; } //메소드 선언 void turnOn() { System.out.println(“폰 전원을 켭니다.”); }
    1
    2
    
      //메서드 선언만 있으면 상속받은 클래스에서는 메서드를 완성해야한다. 
      void internetSearch();
    
    void turnOff() { System.out.println(“폰 전원을 끕니다.”); }

    }

    // 자식 클래스 public class SmartPhone extends Phone { //생성자 선언 SmartPhone(String owner) { //Phone 생성자 호출 super(owner); //부모클래스의 생성자가 있으므로 생성해야한다. }

    //메소드 선언
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
      //메서드 선언만 있으면 상속받은 클래스에서는 메서드를 완성해야한다. 
      	void internetSearch() {
      		System.out.println("인터넷 검색을 합니다.");
        
      // 자식 클래스에서 추상클래스의 메서드를 오버라이딩 할 수 있다.
      @override
      void turnOff() {
      		System.out.println("스마트폰 전원을 끕니다.");
      	}   }
    

    public class PhoneExample { public static void main(String[] args) { //Phone phone = new Phone();

    SmartPhone smartPhone = new SmartPhone(“홍길동”); smartPhone.turnOn(); //전원을 킵니다. smartPhone.internetSearch(); //인터넷 검색을 합니다. smartPhone.turnOff(); //스마트폰 전원을 끕니다. }

    }

1.9 this와 super

 thissuper
메서드현재 객체가 선언된 클래스의 메서드현재 객체의 부모클래스
필드현재 객체의 필드현재 객체의 부모 메서드
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
package study.oop.clazz;

public class TestThis {

  static class A {
    String name = "A";
    boolean working = true;

    void print() {
      System.out.println("A.print():");
      System.out.printf("  => this.name(%s)\n", this.name);
      System.out.printf("  => this.working(%s)\n", this.working);
    }

    void m1() {

      System.out.println("super.m1() 메서드=> A.m1()");
    }
  }


  static class A2 extends A {
    String name = "A2";
    int age = 20;

    @Override
    void print() {
      System.out.println("A2의 print문");
      System.out.printf("this.name 필드 = > %s\n", this.name);
      this.m1();
      System.out.printf("super.name 필드 = > %s\n", super.name);
      super.m1();
    }

    @Override
    void m1() {
      System.out.println("A2.m1()");
    }
  }


  static class A3 extends A2 {
    String name = "A3";
    String gender = "남";

    @Override
    void m1() {
      System.out.println("this.m1() 메서드=> A3.m1()");
    }
  }

  public static void main(String[] args) {
    A2 obj = new A3();
    obj.print();
  }
}
  • 결과

    A2의 print문
    this.name 필드 = > A2
    this.m1() 메서드=> A3.m1()
    super.name 필드 = > A
    super.m1() 메서드=> A.m1()

    • 메서드의 경우 override가 필요하여 A3로 선언된 오버라이딩이 우선
  • 필드의 경우 override가 없으니 A2가 우선
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.