JAVA

상속

쿠키는고양이 2022. 8. 28. 15:46

 

상속이란 한자를 풀어보면 "뒤를 이어준다" 라는 뜻이 된다.

객체지향프로그래밍에 맞춰서 다시 생각해 보면 어떤 객체가 가지고 있던 변수나 메소드를 다른 클래스가 물려받아

사용할 수 있다.

 

 

1. 상속의 개념

상속을 사용하기 위해서는 두 개 이상의 클래스가 필요하다.

최소한 물려줄 클래스 1개, 물려준 걸 받을 클래스 1개가 필요한데 이런 관계를 '조상, 후손' 또는 '부모, 자식' 으로

부른다. 이번 포스팅에서는 부모, 자식을 주 명칭으로 사용하고자 한다.

 

우선 상속을 사용하는 문법은 다음과 같다.

class Point {
	int x;
	int y;
}

class Point3D extends Point		// Point 클래스를 상속받은 Point3D 클래스 선언
{
	int z;

}
 

아래쪽 클래스의 extends 라는 문장을 눈여겨 살펴보자.

extend란 확장하다 라는 뜻으로 사용된다. 즉, Point 클래스를 확장하여 Point3D 클래스를 만들겠다는 뜻이다.

여기서 관계를 살펴보면 아래와 같다.

부모 : Point

자식 : Point3D

 

위의 소스를 좀 더 확장하여 좀 더 알아보자.

package com.inheritance;

public class InhMain {

	public static void main(String[] args) {
		//TODO Auto-generated method stub
		Point p1 = new Point();
		p1.x = 100;
		p1.y = 200;
		
		System.out.println("x = " + p1.x);
		System.out.println("y = " + p1.y);
		
		Point3D p3d1 = new Point3D();
		p3d1.x = 10;
		p3d1.y = 20;
		p3d1.z = 50;
		
		System.out.println("x = " + p3d1.x);
		System.out.println("y = " + p3d1.y);
		System.out.println("z = " + p3d1.z);
	}
}

class Point {
	int x;
	int y;
	public Point() {
		System.out.println("Point 생성자 입니다.");
	}
}

class Point3D extends Point		// Point 클래스를 상속받은 Point3D 클래스 선언
{
	int z;
	public Point3D() {
		System.out.println("Point3D 생성자 입니다.");
	}
}

 

소스코드 아래쪽에 Point 클래스와 Point3D 클래스가 있다.

그리고 코드 위의 main 쪽을 보면 각 클래스의 인스턴스를 만들어 갑을 입력하는 것을 볼 수 있다.

 

그런데 Point3D 형 p3d1 인스턴스를 보면 Point3D 의 인스턴스 변수는 z 밖에 없지만 실제 사용된건 x, y, z 를 모두 사용하고 있다. 즉, x, y 를 가진 Point 클래스의 멤버변수를 물려받아 사용하고 있다는 것을 알 수 있다.

아래의 결과를 보면 바로 확인할 수 있다.

그런데 결과를 보면 이상한 점이 있다.

코드에서 만들어진 인스턴스는 [p1, p3d1] 2개인데 Point 클래스의 생성자가 두 번 호출된 것을 확인할 수 있다.

즉, 자식 클래스의 인스턴스가 생성되면 우선 부모의 생성자가 먼저 호출되고 그 다음 자식 생성자가 호출되어

부모 클래스의 변수의 메소드들이 먼저 메모리에 올라가고 그 다음으로 자식 클래스의 변수와 메소드가 메모리에

올라간다는 것을 확인할 수 있다.

 

 

2. protected (접근 지정자와 상속)

이번에는 위의 예제를 수정하여 상속으로 어디까지 받을 수 있을까 살펴보려한다.

※ public 으로 선언되면 의미가 없기에 public 은 정리에서 제외한다.

 

1) 부모 클래스의 맴버 변수가 private

package com.inheritance;

public class InhMain {

	public static void main(String[] args) {
		 //TODO Auto-generated method stub
		Point p1 = new Point();
		p1.setX(100);
		p1.setY(200);
		
		System.out.println("x = " + p1.getX());
		System.out.println("y = " + p1.getY());
		
		Point3D p3d1 = new Point3D();
		p3d1.setX(10);
		p3d1.setY(20);
		p3d1.setZ(30);
		
		System.out.println("x = " + p3d1.getX());
		System.out.println("y = " + p3d1.getY());
		System.out.println("z = " + p3d1.getZ());
	}

}

class Point {
	private int x;
	private int y;
	
	public Point() {
		System.out.println("Point 생성자 입니다.");
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}
}

class Point3D extends Point		// Point 클래스를 상속받은 Point3D 클래스 선언
{
	private int z;
	
	public Point3D() {
		System.out.println("Point3D 생성자 입니다.");
	}

	public int getZ() {
		return z;
	}

	public void setZ(int z) {
		this.z = z;
	}	
}
 

위의 예제를 보면 각 클래스의 멤버변수는 private 로 선언되어 있다. 그리고 main() 에서 getter/setter 메소드로

각 인스턴스 변수에 값을 넣어주는 것을 확인할 수 있다.

실행 결과를 보면 전혀 이상 없이 접근할 수 있는 것을 확인할 수 있다.

즉, 부모 클래스의 멤버 변수를 private 로 선언해도 상속받은 자식 클래스도 사용할 수 있다.

 

이번에는 자식 클래스에 새로 메소드를 추가하여 보자.

class Point3D extends Point		// Point 클래스를 상속받은 Point3D 클래스 선언
{
	private int z;
	
	public Point3D() {
		System.out.println("Point3D 생성자 입니다.");
	}

	public int getZ() {
		return z;
	}

	public void setZ(int z) {
		this.z = z;
	}
	
	public void inputData() {
		// x = 100;  // error!
		// y = 100;  // erroe!
		setX(100);
		setY(100);
	}
}
 

inputData() 라는 자식 클래스의 메소드에서 직접 부모의 x와 y 를 사용하는 것은 불가하여 에러가 발생된다.

즉, 부모의 private 변수에 접근하기 위해서는 getter / setter 메소드로 접근해야만 한다.

2) 부모 클래스의 맴버 변수가 protected 로 선언

지난 포스팅에서 private와 public에 대해서는 정리를 하였지만 protected 는 정리하지 않았다.

드디어 상속을 정리하게 되며 protected가 등장하게 되었다.

 

우선 protected란 말 그대로 보호하다란 의미이다.

protected 는 자신 외에는 무조건 안되는 private와 모두 허용해주는 public 의 중간단계의 성격을 지닌다.

즉, protected는 같은 패키지 내에 있거나 상속받은 경우에만 접근할 수 있다.

package com.inheritance;

public class InhMain {

	public static void main(String[] args) {
		 //TODO Auto-generated method stub
		Point p1 = new Point();
		p1.setX(100);
		p1.setY(200);
		
		System.out.println("x = " + p1.getX());
		System.out.println("y = " + p1.getY());
		
		Point3D p3d1 = new Point3D();
		p3d1.setX(10);
		p3d1.setY(20);
		p3d1.setZ(30);
		
		p3d1.x = 10000;
		p3d1.y = 20000;
		p3d1.z = 30000;
		
		System.out.println("x = " + p3d1.getX());
		System.out.println("y = " + p3d1.getY());
		System.out.println("z = " + p3d1.getZ());
	}
}

class Point {
	protected int x;
	protected int y;
	
	public Point() {
		System.out.println("Point 생성자 입니다.");
	}

	public int getX() {
		return x;
	}

	public void setX(int x) {
		this.x = x;
	}

	public int getY() {
		return y;
	}

	public void setY(int y) {
		this.y = y;
	}
}

class Point3D extends Point		// Point 클래스를 상속받은 Point3D 클래스 선언
{
	int z;
	
	public Point3D() {
		System.out.println("Point3D 생성자 입니다.");
	}

	public int getZ() {
		return z;
	}

	public void setZ(int z) {
		this.z = z;
	}
	
	public void inputData() {
		x = 100;  // 부모에서 protected로 선언되어 문제없다!
		y = 100;
		setX(100);
		setY(100);
	}
}
 

소스코드에서 보면 우선 자식클래스의 inputData 메소드의 에러가 사라지는 것을 확인할 수 있다.

그 이유는 부모클래스의 멤버 변수가 protected로 선언되었기 때문이다.

또한 main() 에서 보면 클래스 외부에서 값을 직접 접근하여 입력하여도 문제가 없는 것을 확인할 수 있다.

즉, 같은 패키지 내에서는 접근을 허용해 주는 것을 볼 수 있다.

 

 

3) static 으로 선언된 변수( 메소드 )

만약 부모 클래스에서 static으로 선언된 클래스 변수와 클래스 메소드도 상속이 가능할까?

이번 소스에서는 메소드 상속 여부와 클래스 변수, 클래스 메소드 상속 여부를 확인해본다.

package com.inheritance;

public class InhMain {

	public static void main(String[] args) {
		 //TODO Auto-generated method stub
		Point3D p3d1 = new Point3D();
		
		p3d1.x = 10000;
		p3d1.y = 20000;
		p3d1.z = 30000;
		p3d1.number = 7777;	// 부모의 클래스 변수에 값 입력
	
		p3d1.Hi();		// 부모의 클래스 메소드 호출
	        p3d1.printNum(p3d1);
      }
}

class Point {
	protected int x;
	protected int y;
	
	static int number;
	
	public static void Hi() {
		System.out.println("Hi~");
	}

}

class Point3D extends Point		// Point 클래스를 상속받은 Point3D 클래스 선언
{	
	int z;
	
	public void printNum(Point3D p3d1) {
		System.out.println("x = " + p3d1.x);
		System.out.println("y = " + p3d1.y);
		System.out.println("z = " + p3d1.z);
		System.out.println("(static)number = " + p3d1.number);
	}
}
 

예상외로 static 으로 선언된 부모의 변수, 메소드까지 문제없이 상속되는 것을 확인할 수 있었다.

 

 

 

앞의 내용을 정리하자면 다음과 같다.

 

· 부모 클래스에서는 기본 생성자를 만드는 것 외에 아무런 작업이 필요없다.

· 상속 관계를 만들기 위해서는 extends 라는 예약어를 사용한다.

· 자식 클래스의 생성자가 호출되면, 자동으로 부모 클래스의 기본 생성자가 실행된다.

· 자식은 부모 클래스의 public, protected 로 선언된 인스턴스 변수 및 클래스 변수와 메소드에 접근 가능하다.