포괄적인 속성/행위를 갖는 클래스로 구현을 하고 싶으면서 독립적인 클래스로 구현하고 싶을 때 클래스 상속을 해줍니다. 즉, 개별적인 클래스를 정의하면서 (1) 반복적인 코드는 최소화 (2) 비슷한 행위는 공통의 인터페이스 사용 (3) 객체를 명확히 구분하고 싶을 때 클래스 상속을 통해 이를 해결할 수 있습니다.
공통점을 갖고 있지만 서로 다른 클래스들을 상속을 기반으로 설계하여 각 객체들간의 동작이나 특성을 통일성있게 유지하며 소스코드와 유지 보수를 쉽게 이룰 수 있습니다.
IS-A(~중의 하나) 관계가 성립할 때 상속을 기반으로 표현할 수 있습니다.
Person 클래스가 super 클래스이며 UnivStudent가 sub 클래스입니다.
보통 Person 클래스의 멤버는 protected 접근 지정자로 선언하여 다른 패키지에 있는 하위 클래스가 멤버에 접근이 가능하도록 해줍니다. 만일 private 접근 지정자로 선언해 주었다면 access 메서드(getter, setter)를 사용하여 접근할 수 있습니다.
//basicpk/Person.java
package basic;
public class Person {
protected int age;
private String name;
//access 메서드
public int getAge() {
return age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
//일반 메서드
public void WhatYourName() {
System.out.println(name);
}
public void HowOldAreYou() {
System.out.println(age+"세");
}
}
자식 클래스인 UnivStudent는 부모 클래스의 멤버와 메서드를 모두 상속받았습니다. UnitStudent에서 멤버를 따로 선언하는 것이 가능합니다.
Person name이 default, public,protected 일 때는 직접 참조가 가능하여 name이라고만 작성해도 됩니다. 하지만 private일 때는 직접 참조가 불가능하여 this.getName()을 통해 접근이 가능합니다.
//basicpk/UnivStudent.java
package basic;
public class UnivStudent extends Person{
public String major;
public void WhoAreYou() {
System.out.println(this.getName());
System.out.println(age+"세"); //age가 protected이니 접근 가능
System.out.println(this.major+"전공");
}
}
위와 마찬가지로 main에서 객체를 선언주고 멤버에 접근할 때 age는 protected이니 s.age로 작성이 가능합니다. 하지만 name은 private이니 s.setName처럼 access 메서드를 통해 접근이 가능합니다.
//basicpk//StudentTest.java
import basic.UnivStudent;
public class StudentTest {
public static void main(String[] args) {
UnivStudent s=new UnivStudent();
s.age(20);
s.setName("홍길동");
s.major="컴퓨터공학";
s.WhoAreYou();
}
}
특별히 상속관계를 명시하지 않았을 때 기본적으로 Object로부터 상속됩니다.
Person 클래스가 super 클래스이며 UnivStudent가 sub 클래스입니다.
toString으로 재정의된 함수임을 나타내줍니다.
//overridingpk/Person.java
package overriding;
public class Person {
protected int age;
protected String name;
public void WhatYourName() {
System.out.println(name);
}
public void HowOldAreYou() {
System.out.println(age+"세");
}
public void WhoAreYou() {
WhatYourName();
HowOldAreYou();
}
@Override //다음의 함수는 toString으로 재정의된 함수이다를 나타내줌
public String toString() {
return name+"("+age+"세)";
}
}
자식 클래스에서 부모 클래스에서와 같은 이름의 메서드를 선언해 주는 것을 메서드 오버라이딩이라고 합니다. WhoAreYou()는 super 클래스에도 있지만 sub 클래스에서도 따로 정의를 하고 싶을 때 다음과 같이 작성이 가능합니다. 자식 클래스에서 WhoAreYou() 메서드를 재정의 해주니 부모한테 상속받은 WhoAreYou()는 은폐됩니다. 하지만 부모 클래스의 WhoAreYou()메서드를 사용을 하고 싶다면 super를 작성해주어 불러올 수 있습니다.
Object 클래스의 메서드인 String toString()도 super 참조변수를 이용하여 재정의가 가능합니다. 문자열에 넣고 싶은 내용도 추가할 수 있습니다.
//overridingpk/UnivStudent.java
package overriding;
public class UnivStudent extends Person{
protected String major;
public void WhoAreYou() {
super.WhoAreYou();
System.out.println(major+"전공");
}
@Override
public String toString() {
return super.toString()+" " +major+"전공 대학생";
}
}
main에서는 문자열을 출력하듯이 생성한 참조변수를 출력해줄 수 있습니다.
System.out.prinln(s)처럼 작성이 가능합니다.
//overridingpk//StudentTest.java
package overriding;
public class StudentTest {
public static void main(String[] args) {
UnivStudent s=new UnivStudent();
s.age=20;
s.name="홍길동";
s.major="컴퓨터공학";
//s.WhoAreYou();
System.out.println(s); //문자열처럼 출력하고 싶을 때
//s는 s.toString()과 같음
System.out.println(s.toString());
}
}
⭐ 상위 클래스의 생성자 호출을 통해 상속받은 멤버 변수들을 초기화 해야합니다.
하위 클래스의 생성자를 미구현시 자동으로 상위 클래스의 인자 없는 생성자가 호출됩니다.
public Person(String name, int age) {
this.age=age;
this.name=name;
}
public UnivStudent(String name, int age, String major){
super(name, age);
}
this.major=major;
UnivStudent s=new UnivStudent("홍길동",20,"컴퓨터공학");
전체 코드는 다음과 같습니다.
//superpk/Person.java
package superpk;
public class Person {
protected int age;
protected String name;
public Person(String name, int age) {
this.age=age;
this.name=name;
}
public void WhatYourName() {
System.out.println(name);
}
public void HowOldAreYou() {
System.out.println(age+"세");
}
public void WhoAreYou() {
WhatYourName();
HowOldAreYou();
}
@Override
public String toString() {
return name+"("+age+"세)";
}
}
//superpk/UnivStudent.java
package superpk;
public class UnivStudent extends Person{
protected String major;
public UnivStudent(String name, int age, String major) {
super(name, age);
this.major=major;
}
public void WhoAreYou() {
super.WhoAreYou();
System.out.println(major+"전공");
}
@Override
public String toString() {
return super.toString()+" " +major+"전공 대학생";
}
}
//superpk//StudentTest.java
package superpk;
public class StudentTest {
public static void main(String[] args) {
UnivStudent s=new UnivStudent("홍길동",20,"컴퓨터공학");
s.WhoAreYou();
}
}
💡 하위 클래스 작성시 유의점
1. private 멤버는 상속은 되나 직접 참조는 불가합니다. -> access 메서드를 이용합니다.
2. 하위 클래스의 생서자 첫 줄에서 상위 클래스의 생성자를 명시적으로 호출합니다.
3. 재정의 되어 은폐된 멤버는 super, 나머지 멤버는 this 참조변수로 접근합니다.