상속(inheritance)

이현주·2023년 7월 29일
0

JAVA

목록 보기
9/12
post-thumbnail

상속(inheritance)

  • 어떤 클래스의 필드와 메소드를 다른 클래스가 물려 받아 사용하는 것
  • 부모클래스가 자식클래스에게 필드와 메소드를 물려 줌
  • 자바에서는 부모클래스를 슈퍼클래스 (super)", 자식클래스를 서브클래스 (sub) 라고 함

상속의 장점

  • 동일한 메소드를 클래스마다 여러 번 정의할 필요가 없음
  • 클래스를 계층 부모 자식 관계로 관리할 수 있음
  • 클래스의 재사용과 확장이 쉬움
  • 새로운 클래스의 작성 속도가 빠름

상속
1.다른 클래스의 기능(메소드)을 물려받아 사용할 수 있는 기능이다.
2.다음 관계가 있는 클래스들을 상속 관계로 만들 수 있다.
1)is a 관계:~은 ~이다.
2)has a 관계:~은 ~을 가지고 있다.

부모 클래스
1.자식 클래스에게 기능(메소드) 를 제공한다.
2.슈퍼 클래스(super)라고 한다.

자식 클래스
1. 부모 클래스의 기능(메소드)을 자신의 것처럼 사용할 수 있다.
2. 서브 클래스(sub)라고 한다.

상속 구조


is_a 관계

사람(슈퍼클래스)은 학생(서브클래스)이다

Student extends Person
(서브클래스 extends 슈퍼클래스)

is a 관계
1. 상속 관계로 만들 수 있는 대표적인 관계이다.
2. 예시
Student is a person. (학생은 사람이다.)
3. 개념
자식클래스 is a 부모 클래스

MainWrapper클래스

package ex01_is_a;

public class MainWrapper {

  public static void main(String[] args) {
   //Student 객체 선언
    Student s = new Student();
    
    // Student 객체 메소드 확인
    s.eat();      //슈퍼클래스로 부터 상속 받은 메소드
    s.sleep();    // 슈퍼클래스로부터 상속 받은 메소드
    s.study();    //본인것

  }

}

Person클래스

package ex01_is_a;

public class Person {

  public void eat() {
    System.out.println("냠냠");
  }
  
  public void sleep()
  {
    System.out.println("드르렁");
  }
}

Student 클래스

package ex01_is_a;


/*
 * 상속 관계 도식
 *  ┌--------------┐
 *  │    Person    │ 슈퍼 클래스
 *  │--------------│
 *  │     eat()    │
 *  │    sleep()   │
 *  └--------------┘
 *          ▲
 *          │
 *          │
 *  ┌--------------┐
 *  │    Student   │ 서브 클래스
 *  │--------------│
 *  │    study()   │
 *  └--------------┘
 */

public class Student extends Person  { //자식 extneds 부모

  public void study() {
    System.out.println("공부");
  }
  
}

has_a 관계

TV는 Remocon을 가지고 있다.
(서브클래스)는 (슈퍼클래스)를 가지고 있다

has a 관계
1. 간혹 상속 관계로 만들 수 있다.
2. 예시
Tv has a Remocon. (Tv는 Remocon을 가지고 있다.)
3. 개념
자식클래스 has a 부모클래스

MainWrapper 클래스

package ex02_has_a;

public class MainWrapper {

  public static void main(String[] args) {
    
    // Tv 객체 선언 & 생성
    Tv tv = new Tv();
    
    // Tv 객체 메소드
    tv.upCh();
    tv.upVol();
    System.out.println(tv.getCh());
    System.out.println(tv.getVol());

    tv.downCh();
    tv.downVol();
    System.out.println(tv.getCh());
    System.out.println(tv.getVol());
    
  }

}

TV 클래스

package ex02_has_a;



public class Tv extends Remocon {

}

Remocon 클래스

package ex02_has_a;

public class Remocon {

  private int ch;  // 0 ~ MAX_CH
  private int vol; // 0 ~ MAX_VOL
  private final int MAX_CH = 99;
  private final int MAX_VOL = 10;
  
  public void upCh() {
    if(ch == MAX_CH) {
      ch = 0;
      return;
    }
    ch++;
  }
  public void downCh() {
    if(ch == 0) {
      ch = MAX_CH;
      return;
    }
    ch--;
  }
  public void upVol() {
    if(vol == MAX_VOL) {
      return;
    }
    vol++;
  }
  public void downVol() {
    if(vol == 0) {
      return;
    }
    vol--;
  }
  
  public int getCh() {
    return ch;
  }
  public void setCh(int ch) {
    this.ch = ch;
  }
  public int getVol() {
    return vol;
  }
  public void setVol(int vol) {
    this.vol = vol;
  }
  
}

상속관계의 생성자 (constructor)

상속 관계의 생성자
1. 자식 클래스를 생성할 땐 "반드시" 부모 클래스를 "먼저" 생성해야 한다.
2. 서브 클래스를 생성할 땐 "반드시" 슈퍼 클래스를 "먼저" 생성해야 한다.
3. 서브 클래스의 생성자가 호출될 때는 "반드시" 슈퍼 클래스의 생성자를 "먼저" 호출해야 한다.
4. 만약 서브 클래스의 생성자에서 슈퍼 클래스의 생성자를 호출하지 않으면 Java에 의해서 슈퍼 클래스의 "디폴트 생성자"가 호출된다.
5. 서브 클래스의 생성자에서 슈퍼 클래스의 생성자를 호출하는 방법은 super()이다.

MainWrapper 클래스

package ex03_constructor;


public class MainWrapper {

  public static void main(String[] args) {
    
    // new Student()를 이용한 Student 객체 생성
    Student s1 = new Student();
    System.out.println(s1.getName());
    System.out.println(s1.getSchool());
    
    // new Student("가산대학교")를 이용한 Student 객체 생성
    Student s2 = new Student("가산대학교");
    System.out.println(s2.getName());
    System.out.println(s2.getSchool());
    
    // new Student("홍길동", "가산대학교")를 이용한 Student 객체 생성
    Student s3 = new Student("홍길동", "가산대학교");
    System.out.println(s3.getName());
    System.out.println(s3.getSchool());
    
  }

}

Person클래스

package ex03_constructor;

public class Person {

  private String name;
  
  // new Person()에서 호출되는 생성자(constructor)
  public Person() {
    System.out.println("Person() 호출");
  }
  // new Person("홍길동")에서 호출되는 생성자(constructor)
  public Person(String name) {
    this.name = name;
    System.out.println("Person(String name) 호출");
  }
  
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  
}

Student 클래스

package ex03_constructor;

public class Student extends Person {

  private String school;
  
  // new Student()에서 호출되는 생성자
  public Student() {
    System.out.println("Student() 호출");
  }
  // new Student("가산대학교")에서 호출되는 생성자
  public Student(String school) {
    this.school = school;
    System.out.println("Student(String school) 호출");
  }
  // new Student("홍길동", "가산대학교")에서 호출되는 생성자
  public Student(String name, String school) {
    // 파라미터 String name을 슈퍼 클래스의 생성자를 호출할 때 전달한다.
    super(name);  // 인수 name은 String 타입이므로 파라미터가 String인 Person 생성자가 호출된다.
    this.school = school;
  }
  
  public String getSchool() {
    return school;
  }
  public void setSchool(String school) {
    this.school = school;
  }
  
}

Override 오버라이드

메소드 오버라이드(method override)
1. 슈퍼 클래스의 메소드를 서브 클래스가 다시 만드는 것을 말한다. (메소드 덮어쓰기)
2. 오버라이드 하는 메소드는 @Override Annotation을 추가하는 것이 좋다. (권장이지만 필수로 하자.)
3. 반드시 동일한 모습으로 오버라이드 해야 한다.

MainWrapper

package ex04_override;

public class MainWrapper {

  public static void main(String[] args) {
    
    // CafeLatte 객체 선언 & 생성
    CafeLatte latte = new CafeLatte();
    
    // CafeLatte 객체 메소드
    latte.taste();

  }

}

CafeLatte 클래스

package ex04_override;


public class CafeLatte extends Espresso {

  @Override  // 오버라이드 한 메소드, 문법 체크를 해 준다.
  public void taste() {
    System.out.println("라떼 맛");
  }
  
}

Espresso 클래스

package ex04_override;

public class Espresso {

  public void taste() {
    System.out.println("쓴 맛");
  }
  
}

upcasting 업캐스팅

upcasting
1. 서브 클래스 객체를 슈퍼 클래스 타입으로 저장할 수 있다.
2. 강제로 캐스팅(형변환)할 필요가 없다. 자동으로 변환된다.
3. 장점
1) 슈퍼 클래스 타입으로 모든 서브 클래스 객체를 저장할 수 있다.
2) 서로 다른 타입의 객체를 하나의 타입으로 관리할 수 있다.
4. 단점
1) 슈퍼 클래스 타입으로 저장하기 때문에 슈퍼 클래스의 메소드만 호출할 수 있다.
2) 이 단점을 해결하기 위해서 메소드 오버라이드(method override)를 이용할 수 있다.

MainWrapper 클래스

 package ex05_upcasting;


public class MainWrapper {

public static void main(String[] args) {

  Person p1 = new Student("고길동", "강원대학교");
  Person p2 = new Alba("홍길동", "가산대학교", "투썸");
  
  p1.eat();
  p1.sleep();
  p1.study();

  p2.eat();
  p2.sleep();
  p2.study();
  p2.working();
  
}

}
 

Alba클래스

package ex05_upcasting;

// 학교 다니는 알바생

public class Alba extends Student {

  private String work;
  
  // new Alba()
  public Alba() {
    super();  // new Student() 호출과 동일하다. 디폴트 생성자이므로 생략할 수 있다.
  }
  // new Alba("홍길동", "가산대학교", "투썸")
  public Alba(String name, String school, String work) {
    super(name, school);  // new Student("홍길동", "가산대학교") 호출과 동일하다.
    this.work = work;
  }
  
  public String getWork() {
    return work;
  }
  public void setWork(String work) {
    this.work = work;
  }
  
  @Override
  public void working() {
    System.out.println("일함");
  }
  
  @Override
  public void info() {
    System.out.println("이름: " + getName());
    System.out.println("학교: " + getSchool());
    System.out.println("직장: " + work);
  }
  
}

Person 클래스

package ex05_upcasting;

public class Person {

  private String name;
  
  // new Person()
  public Person() {
    
  }
  // new Person("홍길동")
  public Person(String name) {
    this.name = name;
  }
  
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  
  public void eat() {
    System.out.println("냠냠");
  }
  public void sleep() {
    System.out.println("쿨쿨");
  }
  // 실행이 목적이 아닌 호출이 목적인 메소드
  public void study() {
    
  }
  // 실행이 목적이 아닌 호출이 목적인 메소드
  public void working() {
    
  }
  
  public void info() {
    System.out.println("이름: " + name);
  }
  
}

Student 클래스

package ex05_upcasting;

// Student is a Person.
// 서브 클래스(자식) is a 슈퍼 클래스(부모)

public class Student extends Person {

  private String school;
  
  // new Student()
  public Student() {
    // 반드시 슈퍼 클래스의 생성자 호출이 있어야 하기 때문에,
    // 개발자가 슈퍼 클래스의 생성자를 호출하지 않으면 Java가 직접 슈퍼 클래스의 생성자를 호출한다.
    // Java가 호출하는 슈퍼 클래스의 생성자는 "디폴트 생성자"만 가능하다.
    super();  // 개발자가 작성하지 않아도 자동으로 호출되는 슈퍼 클래스의 디폴트 생성자
  }
  // new Student("홍길동", "가산대학교")
  public Student(String name, String school) {
    // new Person("홍길동") 호출을 위한 코드
    super(name);
    this.school = school;
  }
  
  public String getSchool() {
    return school;
  }
  public void setSchool(String school) {
    this.school = school;
  }
  
  @Override
  public void study() {
    System.out.println("공부");
  }
  
  @Override
  public void info() {
    System.out.println("이름: " + getName());
    System.out.println("학교: " + school);
  }
  
}

downcasting 다운 캐스팅

MainWrapper 클래스

package ex06_downcasting;

public class MainWrapper {

  public static void ex01() {
    
    Person p = new Student();
    p.eat();
    p.sleep();
    ((Student)p).study();  // 슈퍼 클래스 타입 -> 서브 클래스 타입으로 변경(다운캐스팅)
    ((Worker)p).work();    // 잘못된 캐스팅을 막고 싶다!
    
  }
  
  public static void ex02() {
    
    Person p = new Student();
    System.out.println(p instanceof Person);  // p가 Person  타입이면 true, 아니면 false
    System.out.println(p instanceof Student); // p가 Student 타입이면 true, 아니면 false
    System.out.println(p instanceof Worker);  // p가 Worker  타입이면 true, 아니면 false
    
  }
  
  public static void ex03() {
    
    Person p1 = new Student();
    if(p1 instanceof Student) {
      ((Student) p1).study();
    }
    
    Person p2 = new Worker();
    if(p2 instanceof Worker) {
      ((Worker) p2).work();
    }
    
  }
  
  public static void main(String[] args) {
    ex02();
  }

}

Person 클래스

package ex06_downcasting;

public class Person {

  public void eat() {
    System.out.println("냠냠");
  }
  
  public void sleep() {
    System.out.println("쿨쿨");
  }
  
}

Student클래스

package ex06_downcasting;

public class Student extends Person {

  public void study() {
    System.out.println("공부");
  }
  
}

Worker클래스

package ex06_downcasting;

public class Worker extends Person {

  public void work() {
    System.out.println("일함");
  }
  
}

Object

java.lang.Object 클래스
1. 모든 클래스의 최상위 슈퍼 클래스이다.
2. 별도의 슈퍼 클래스를 명시하지 않은 클래스들은(extends가 없는 클래스) 모두 Object 클래스의 서브 클래스이다.
3. 모든 것을 저장할 수 있는 Java의 만능 타입이다.
4. Object 타입으로 저장하면 Object 클래스의 메소드만 호출할 수 있다. 이를 해결하기 위해서 "반드시" 캐스팅을 해야 한다.

MainWrapper 클래스

package ex07_Object;

public class MainWrapper {

  public static void ex01() {
    
    // Object 타입으로 모든 객체를 저장할 수 있다.
    // Object 타입으로 저장한 객체를 사용할 때는 캐스팅 해야 한다.

    Object obj = new Person();
    ((Person)obj).setName("홍길동");
    ((Person)obj).setAge(20);
    System.out.println(((Person)obj).getName());
    System.out.println(((Person)obj).getAge());
    
  }
  
  public static void ex02() {
    
    // 동일한 객체 2개
    Person p1 = new Person("홍길동", 20);
    Person p2 = new Person("홍길동", 20);
    
    // 동일한 객체인지 판단
    boolean same = p1.equals(p2);
    
    // 결과 확인
    System.out.println(same ? "동일한 객체" : "다른 객체");
    
  }
  
  public static void ex03() {
    
    Person p = new Person("홍길동", 20);
    System.out.println(p);  // 객체 p는 p.toString()이 자동 호출되서 사용된다.
    
  }
  
  public static void main(String[] args) {
    ex03();
  }

}

Person 클래스

package ex07_Object;

import java.util.Objects;



/*
 *  ┌--------------┐
 *  │  Object      │ 슈퍼 클래스
 *  │--------------│
 *  │  equals()    │ 두 객체의 참조값을 비교해서 같으면 true, 아니면 false 반환
 *  │  getClass()  │ 어떤 클래스인지 반환
 *  │  hashCode()  │ int 타입의 해시코드값, Object 클래스는 객체의 참조값을 해시코드값으로 사용함
 *  │  toString()  │ "클래스이름@참조값" 형식의 문자열을 반환
 *  │  notify()    │ 스레드(thread) 관련 메소드
 *  │  wait()      │ 스레드(thread) 관련 메소드
 *  └--------------┘
 *          ▲
 *          │
 *          │
 *  ┌--------------┐
 *  │  Person      │ 서브 클래스
 *  │--------------│
 *  │  @Override   │
 *  │  equals()    │ 이름과 나이가 같으면 true, 아니면 false 반환 (하드코딩하지 않고, 자동완성한다.)
 *  │              │
 *  │  @Override   │
 *  │  toString()  │ 이름과 나이를 확인할 수 있는 문자열 반환 (하드코딩하지 않고, 자동완성한다.)
 *  └--------------┘
 */

public class Person {

  private String name;
  private int age;
  
  // new Person()
  public Person() {
    
  }
  // new Person("홍길동", 20)
  public Person(String name, int age) {
    this.name = name;
    this.age = age;
  }
  
  @Override
  public int hashCode() {
    return Objects.hash(age, name);
  }
  @Override
  public boolean equals(Object obj) {
    if (this == obj)  // p1.equals(p1)
      return true;
    if (obj == null)  // p1.equals(null)
      return false;
    if (getClass() != obj.getClass())  // p1.equals(s1)
      return false;
    Person other = (Person) obj;
    return age == other.age && Objects.equals(name, other.name);
  }
  
  @Override
  public String toString() {
    return "Person [name=" + name + ", age=" + age + "]";
  }
  
  public String getName() {
    return name;
  }
  public void setName(String name) {
    this.name = name;
  }
  public int getAge() {
    return age;
  }
  public void setAge(int age) {
    this.age = age;
  }
  
}

abstract 추상 클래스

추상 클래스
1. 추상 메소드를 1개 이상 가지고 있는 클래스이다.
2. abstract 키워드를 추가한다.
3. 추상 클래스는 객체를 생성할 수 없다. (미완성 된 클래스이기 때문이다.)
4. 추상 클래스의 서브 클래스는 "반드시" 추상 메소드를 오버라이드 해야 한다.

MainWrapper 클래스

package ex08_abstract;

public class MainWrapper {

  public static void main(String[] args) {
    
    // 추상 클래스 Person은 객체를 생성할 수 없다.
    // Person p1 = new Person();
    // p1.eat();
    // p1.sleep();
    
    Person p2 = new Student();
    p2.eat();
    p2.sleep();
    p2.study();
    
  }
  
}

person 클래스

package ex08_abstract;



public abstract class Person {

  public void eat() {
    System.out.println("냠냠");
  }
  public void sleep() {
    System.out.println("쿨쿨");
  }
  
  // 호출을 위해서 생성한 study
  // 본문이 필요 없기 때문에 본문이 없는 메소드로 만들 수 있다.
  // 본문이 없는 메소드를 "추상 메소드"라고 한다.
  // abstract 키워드를 추가하고 본문({})을 제거한다.
  public abstract void study();
  
}

Study

package ex08_abstract;

public class Student extends Person {

  @Override
  public void study() {
    System.out.println("공부");
  }
  
}

interface 인터페이스

인터페이스
1. JDK 1.7 이전에는 "추상 메소드 + final 상수"로만 구성된 추상 클래스를 의미했다.
2. JDK 1.8 이후로는 "추상 메소드 + default 메소드 + static 메소드 + final 상수"로 구성된다.
1) 추상 메소드 : 본문이 없는 메소드 (대부분은 추상 메소드로 구성됨)
2) default 메소드 : 본문이 있는 메소드
3) static 메소드 : 클래스 메소드 (본문 있음)
3. 인터페이스의 추상 메소드는 public abstract를 생략할 수 있다.

클래스 상속 vs 인터페이스 구현

1. 클래스를 상속 받는다.
public class Person { }
public class Student extends Person { }
2. 인터페이스를 구현한다.
public interface Shape { }
public class Rectangle implements Shape { }

MainWrapper클래스

package ex09_interface;

public class MainWrapper {

  public static void main(String[] args) {
    
    Shape s1 = new Rectangle(3, 4);
    System.out.println(s1.getArea());
    s1.info1();
    Shape.info2();
    
    Shape s2 = new Circle(1.5);
    System.out.println(s2.getArea());
    s2.info1();
    Shape.info2();

  }

}

Circle 클래스

package ex09_interface;

public class Circle implements Shape {

  private double radius;
  
  public Circle(double radius) {
    this.radius = radius;
  }
  
  @Override
  public double getArea() {
    return PI * radius * radius;
  }

}

Rectangle 클래스

package ex09_interface;


public class Rectangle implements Shape {

  private int width;
  private int height;
  
  public Rectangle(int width, int height) {
    this.width = width;
    this.height = height;
  }
  
  // 인터페이스를 구현한 클래스는 "반드시" 추상 메소드를 오버라이드 해야 한다.
  @Override
  public double getArea() {
    return width * height;
  }
  
}

Shape 클래스

package ex09_interface;



public interface Shape {

  // final 상수
  public static final double PI = 3.14;
  
  // 추상 메소드
  double getArea();  // public abstract가 생략된 모습
  
  // default 메소드
  public default void info1() {
    System.out.println("나는 도형이다.");
  }
  
  // static 메소드
  public static void info2() {
    System.out.println("나는 도형이다.");
  }
  
}

관련예제

깃허브 주소
https://github.com/000000hj/javastudy

profile
졸려요

0개의 댓글