제이제이
article thumbnail

 

본 내용은 "윤성우의 열혈 Java 프로그래밍"를 참고로 정리한 내용입니다.

 

1. 상속


❓ 상속이란?

 🔥 상속이란 “서로 연관된 클래스에 대해 공통적인 규칙, 규약들을 정의한 것”입니다.

 

  • 상속에 대해서 처음 배우게 되었을 때 학교 수업 혹은 강의에서 상속은 “코드의 재활용을 위한 문법이다"라고 배우는 경우가 있습니다.
  • 그러나 상속은서로 연관된 클래스에 대해 공통된 규칙, 규약들을 정의한 것으로 생각해야 합니다.
    이 내용을 알고나서 상속에 대해서 자세히 살펴봅시다.

상속의 특성

상속을 간단하게 설명하자면,기존에 정의된 클래스에 메소드와 변수를 추가하여 새로운 클래스를 정의하는 것입니다.

코드를 통해 상속의 특성에 대해서 알아봅시다.

class Man{
	String name; //이름
	
         //메소드 tellYourName 정의함
	public void tellYourName(){
	  System.out.println("My Name is " + name);
	}
}

 

위의 클래스를 상속하여 아래와 같이 새로운 클래스를 정의할 수 있습니다.

class BusinessMan extends Man{ //Man 클래스를 상속하는 BusinessMan 클래스
	String company;
	String position;
    
	//tellYourNameInfo라는 메소드를 정의함
	public void tellYourNameInfo(){
		System.out.println("My company is " + company);
		System.out.println("My position is " + position);
		tellYourName();//Man 클래스를 상속했기 때문에 tellYourName 메소드 호출 가능 			
 }
}

 

🔥 키워드 extends는 상속을 의미하는 키워드입니다.
  • extends Man은 Man 클래스를 상속한다는 의미입니다.

 

Man 클래스를 상속하는 BusinessMan 클래스의 인스턴스를 생성하면 다음 그림과 같은 형태의 인스턴스가 생성됩니다.

BusinessMan man = new BusinessMan();

BusinessMan의 참조 변수 man

 

🔥 상속된 BusinessMan 클래스의 인스턴스에는 Man 클래스의 변수와 메소드가 존재합니다.
  • 이 때문에 BusinessMan 클래스 안에서는 tellYourName 메소드를 호출할 수 있습니다.

 

상속의 대상이 되는 클래스와 상속을 하는 클래스를 부르는 명칭

 💡 상속이 되는 클래스를 주로 부모 클래스, 상속을 받는 클래스를 자식 클래스라고 합니다.

 

  • 상속의 대상이 되는 클래스 = 상위 클래스, 부모 클래스
    • ex) Man 클래스
  • 상속을 하는 클래스 = 하위 클래스, 자식 클래스
    • ex) BusinessMan 클래스
 🔥 상속의 관계는 UML기호를 통해 클래스 설계단계에서 널리 사용함으로 꼭 기억해두어야 합니다.

UML 기호로 표시한 상속 관계

  • 화살촉 부분은 하위클래스(자식 클래스)에서 상위 클래스(부모 클래스) 부분으로 향하게 되어있습니다.

 

상속과 생성자의 관계

이번에는 상속과 생성자의 관계를 살펴봅시다.

이전 코드인 Man클래스의 생성자를 다음 코드에 정의하였습니다.

class Man{
		String name;
		
        //생성자 정의
		public Man(String name){
			this.name = name;
		}

		public void tellYourName(){
			System.out.println("My Name is" + name);
	 }
}

 

BusinessMan 클래스 또한 생성자를 코드에 정의하였습니다.

class BusinessMan extends Man{
	String company;
	String position;

	//생성자 정의
	public BusinessMan(String company, String position){
		this.company = company;
		this.postion = position;
  }
	
	public void tellYourNameInfo(){
		System.out.println("My company is" + company);
		System.out.println("My position is" + postion);
		tellYourName();
  }
}

 

코드상으로는 문제가 없을 것 같은데 상속으로 인해 다음과 같은 문제점이 발생됩니다.

 🚨 Business의 인스턴스에는 Man클래스의 상속을 통해 Man의 멤버가 담겨 있습니다.

 

String name;//상속으로 인해 BusinessMan 클래스의 멤버가 된 변수

 

따라서 다음과 같이 BusinessMan의 생성자에서도 name 변수를 초기화 해야 합니다.

class Man{
    String name;
    public void tellYourName(){
        System.out.println("My name is " + name);
    }
}

class BusinessMan extends Man{
    String company;
    String position;

    public BusinessMan(String name,String company, String position) {
        //상위 클래스 man의 멤버 초기화
        this.name = name;

        //클래스 BusinessMan의 멤버 초기화
        this.company = company;
        this.position = position;
    }

    public void tellYourInfo(){
        System.out.println("My company is " + company);
        System.out.println("My position is" + position);
        tellYourName();
    }
}

public class MyBusinessMan  {
    public static void main(String[]args){
        BusinessMan man = new BusinessMan("YOON","Hybrid ELD","Staff Eng");
        man.tellYourInfo();

    }
}

 

출력 결과

My company is Hybrid ELD
My position isStaff Eng
My name is YOON
  • 코드상으로는 문제가 없으나 Man 클래스의 생성자가 정의되어 있지 않습니다.(질이 나쁜 코드입니다.)
    따라서 Man 클래스에 대해 생성자가 있을 경우 어떻게 되는지에 대해 알아볼 필요가 있습니다.

 

상속 관계에 있는 생성자의 호출 관계

이번에는 상속 관계에 있는 생성자의 호출에 대해 다음의 코드를 통해 자세히 알아봅시다.

class SuperCLS{
	//부모 클래스의 생성자
    public SuperCLS() { 
        System.out.println("I'm Super Class");
    }
}

class SubCLS extends SuperCLS{
	// 자식 클래스의 생성자
    public SubCLS(){ 
        System.out.println("I'm Sub Class");
        }
     }

public class SuperSubCon {
    public static void main(String[]args){
        new SubCLS();
  }
}

 

출력 결과

I'm Super Class
I'm Sub Class

 

출력 결과를 통해 2가지 사실을 알 수 있습니다.

 💡 1. 자식 클래스의 인스턴스를 생성할 때 부모클래스와 자식 클래스의 생성자 둘 모두 호출됩니다.
       2. 자식 클래스의 인스턴스를 생성할 때 부모 클래스의 생성자가 먼저 호출됩니다.

 

이를 통해 자식 클래스의 생성자를 다음과 같이 이해하면 됩니다.

(superCLS( )는 부모 클래스의 생성자가 호출됨을 의미합니다.)

class SubCLS extends SuperCLS{
	public SubCLS() {
		superCLS(); //부모 클래스의 생성자가 호출됨
		System.out.println("I'm Sub Class");
  }
}

 

🔥 보통 이를(상위 클래스의 생성자)보다 명시적으로 호출하기 위해 super라는 키워드를 사용합니다.

 

보통 상위 클래스의 생성자를 호출할 때는 키워드 super를 통해 사용하는데 다음의 코드를 살펴봅시다.

class SuperCLS {
    public SuperCLS() {
        System.out.println("Con: SuperCLS()");
    }
    public SuperCLS(int i){
        System.out.println("Con: SuperCLS(int i)");
    }
    public SuperCLS(int i, int j){
        System.out.println("Con: SuperCLS(int i, int j)");
    }
}

class SubCLS extends SuperCLS{
    public SubCLS() {
        System.out.println("Con: SubCLS()");
    }

    public SubCLS(int i) {
        super(i); //상위 클래스의 생성자를 지정 및 호출
        System.out.println("Con: SubCLS(int i)");
    }

    public SubCLS(int i, int j) {
        super(i, j); //상위 클래스의 생성자 지정 및 호출
        System.out.println("Con: SubCLS(int i, int j)");
    }
}

public class SuperSubCon {
    public static void main(String[]args){
        System.out.println("1. ");
        new SubCLS();
        System.out.println();

        System.out.println("2. ");
        new SubCLS(1);
        System.out.println();

        System.out.println("3. ");
        new SubCLS(1,2);
        System.out.println();
    }
}

 

출력 결과

Con: SuperCLS()
Con: SubCLS()

2. 
Con: SuperCLS(int i)
Con: SubCLS(int i)

3. 
Con: SuperCLS(int i, int j)
Con: SubCLS(int i, int j)

 

분석

위의 코드를 분석해봅시다.

먼저 자식 클래스인 SupCLS클래스의 생성자를 살펴봅시다.

    public SubCLS(int i) {
        super(i); //상위 클래스의 생성자 호출을 의미합니다.
        System.out.println("Con: SubCLS(int i)");
    }

    public SubCLS(int i, int j) {
        super(i, j); //상위 클래스의 생성자 호출을 의미합니다.
        System.out.println("Con: SubCLS(int i, int j)");
    }
  • 이렇게 자식 클래스(하위 클래스)에서 사용된 키워드 super부모 클래스(상위 클래스)의 생성자 호출을 의미합니다.
🚨 단, 부모 클래스(상위 클래스)의 생성자를 의미하는 키워드 super가 자식 클래스의 생성자의 첫 문장에 있어야 합니다.

 

public SubCLS(int i){
		System.out.println(~~~);
		super(i); //이 위치에 위치해 있으면 컴파일 오류 발생합니다.
}

 

상속 관계에 있는 클래스의 적절한 생성자 정의

앞서 살펴봤던 Man클래스와 Man클래스이 자식 클래스인 Business 클래스에 키워드 super를 이용해 생성자를 정의해 봅시다.

class Man{
    String name;

    public Man(String name) {
        this.name = name;
    }
    public void tellYourName(){
        System.out.println("My name is " + name);
    }
}

class BusinessMan extends Man{
    String company;
    String position;

    public BusinessMan(String name, String company, String position) {
        super(name); //상위 클래스의 생성자를 호출합니다.
        this.company = company;
        this.position = position;
    }

    public void tellYourInfo(){
        System.out.println("My company is" + company);
        System.out.println("My position is " + position);
        tellYourName();
    }
}

public class MyBusinessMan {
    public static void main(String[]args){
        BusinessMan man = new BusinessMan("YOON","Hybrid ELD","Staff Eng.");
        man.tellYourInfo();
    }
}

 

출력 결과

My company isHybrid ELD
My position is Staff Eng.
My name is YOON

 

🔥 위의 코드를 통해 상속 관계에 있을지라도 인스턴스 변수는 각 클래스의 생성자를 통해서
      초기화해야 함을 알 수 있습니다.

 

단일 상속만 지원하는 자바

자바 프로그램은 코드가 복잡해지는 것을 막기 위해 단일 상속만을 지원합니다.

(하나의 클래스가 상속할 수 있는 클래스의 수는 최대 하나라는 것을 의미합니다.)

class AAA{ ~~}
class ZZZ extends AAA{ ~~~}

그러나 다음과 같이 상속한 클래스를 상속하는 것은 가능합니다.

class AAA { ~~~ }
class MMM extends AAA{ ~~~ }
class ZZZ extends MMM{ ~~~ }

 

2. 클래스 변수와 클래스 메소드의 상속 관계


❓ static 선언이 있는 클래스 변수와 클래스 메소드도 상속 관계에 포함될까요?

static 선언이 있는 클래스 변수와 클래스 메소드에 대해 다시 생각해봅시다.

static선언이 있는 클래스 변수와 클래스 메소드

  • 인스턴스(객체)의 생성과 상관없이 접근이 가능합니다.
  • 클래스 내부와 외부에서 (접근 수준 지시자가 허용하면)접근이 가능합니다.
  • 클래스 변수와 클래스가 위치한 클래스 안에서는 직접 접근이 가능하다.
🔥 따라서 클래스 변수와 클래스 메소드는 상속의 대상이 아닙니다.

 

이와 관련해서 코드를 살펴봅시다.

class SuperCLS{
		static int count = 0; //클래스 변수
		public SuperCLS(){
			count++; //클래스 내에서는 직접 접근이 가능
	}
}

위의 클래스 내에서 선언된 static변수(클래스 변수)는 SuperCLS의 인스턴스 내에 존재하지 않습니다.

따라서 클래스 변수와 클래스 메소드는 상속의 대상이 아닌 것을 확인할 수 있습니다.

그런데 다음의 질문에 대해서는 생각해 볼 필요가 있습니다.

❓ 그렇다면 부모 클래스에 위치한 클래스 변수와 클래스 메소드를 자식 클래스에서 접근이 가능할까?

 

이를 확인하기 위해 다음의 코드를 살펴봅시다.

class SuperCLS3{
    protected static int count = 0; //protected는 하위 클래스 접근을 허용

    public SuperCLS3() {
        count++;
    }
}

class  SubCLS3 extends SuperCLS3{
    public void showCount(){
        //상위 클래스에 위치한 클래스 변수 count에 접근
        System.out.println(count);
    }
}

public class SuperSubStatic {
    public static void main(String[]args){
        SuperCLS3 obj1 = new SubCLS3(); //count 값 1증가
        SuperCLS3 obj2 = new SubCLS3(); //count 값 1증가

        //아래 인스턴스 생성 과정에서 SuperCLS 생성자 호출되므로,
        SubCLS3 obj3 = new SubCLS3(); //count 값 1증가
        obj3.showCount();
    }
}

 

출력 결과

3

 

결론

위의 코드를 통해 부모 클래스에 있는 static 변수 count를 하위 클래스에서 이름만으로도 접근할 수 있다는 확인할 수 있습니다.

 🔥 자식 클래스(하위 클래스)에서도 접근 지시자가 허용된다면
        부모 클래스(상위 클래스)의 클래스 변수, 클래스 메소드에 이름으로 접근이 가능합니다.

 

📒 Reference (참고 자료)


  1. 윤성우의 열혈 자바
profile

제이제이

@아사비치즈스틱

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!