본문 바로가기
Kosta DevOps 과정 280기/Java

객체 지향 프로그래밍 특징2 - 상속성

by 롯슈83 2024. 5. 20.

상속성(Inheritance)

  • 이미 정의되어 있는 클래스를 확장하여(상속하여) 새로운 클래스를 만들 수 있다.
  • 코드의 재사용성을 높일 수 있다.
  • 부모 클래스(=상위 클래스=조상 클래스) : 상속을 해 준 클래스
  • 자식 클래스(= 하위 클래스=후손 클래스): 상속을 받은 클래스
    • 부모의 속성 및 메소드는 모두 상속받는다. 따라서 자식 클래스는 달라지는 것만 상속받으면 된다.
    • 부모의 속성 및 메소드를 자기 것처럼 쓸 수 있다. (자식한테 없으면 부모것을 쓰고, overriding 하면 자식것을 쓴다)
  • 형태
    • class 부모 클래스{
           내용들
      }
    • class 자식 클래스 extends 부모클래스{
            
      }
  • 이렇게 함으로써 자식클래스는 부모 클래스의 모든 속성과 동작을 물려받게 된다.
  • 부모 클래스의 코드양이 몇백줄이 된다고 할 때 extends 부모 클래스 하는 순간 자식 클래스에서 그 코드를 모두 작성한 꼴이 된다. ==> 그래서 상속을 함으로 해서 코드의 재사용성을 높일 수 있다. 
  • 또 우리 현실에서 동물이라는 단어보다는 쥐, 말, 토끼, 뱀이라는 단어를 먼저 사용했을 겁니다. 쥐, 말, 토끼, 뱀 등이 공통으로 가지는 속성과 동작을 뽑아서 일반화하여 동물이라고 하자고 했을 겁니다. 이와 같이, 
  • 미래에 만들 클래스들의 공통적이고 일반적인 속성과 동작을 뽑아서 일반화 시킨다.(부모 클래스 만들기)
  • 그것에 확장을 하여 자식을 만든다.(고유한 기능과 속성)
package com.kosta.exam01;
class A{
	String title;
	int year;
}
class B extends A{
	String data;
	
	public void info() {
		System.out.println(title +", "+year+", "+data);
	}
}

//B는 A속에 있는 것을 모두 물려받는다.
//B는 data 멤버 변수도 가진다.

public class D02Inheriance {
	public static void main(String[] args) {
		B ob2 = new B();
		//new B()해서 그 안에는 title, year, data 만큼 메모리가 잡힌다.
		
		ob2.title = "hello";
		ob2.year = 3;
		ob2.data = "java";
		System.out.println(ob2.title+", "+ob2.year+", "+ob2.data);
		ob2.info();
	}
}
  • 상속관계는 is a 관계에 있다고 한다. 즉, 자식은 부모에 포함된다. 자식 클래스의 자료형은 부모 클래스의 자료형에 포함될 수 있고, 그 반대는 될 수 없다. 
  • 상속 관계에 있을 때 부모 클래스의 참조 변수가 자식 클래스의 객체를 참조할 수 있다.
    이 때, 참조변수로
    - 부모클래스에는 있고 자식클래스에는 없는 메소드를 호출할 수 있다.
    - 부모클래스에는 있고 자식클래스에도 있는 메소드를 호출하면 자식클래스에 있는 메소드가 동작한다.
    - 부모클래스에는 없고 자식클래스에도 있는 메소드를 호출하려면 참조변수를 자식클래스를 참조하는 변수로 형 변환한 후에 호출해야한다.
  • 아래 코드에서 Shape 는 Rectangle 과 Circle 의 부모 클래스이다. 부모 클래스의 참조변수는 자식 클래스의 객체를 참조할 수 있다. 만약 Shape가 부모가 아닌 조상일 때도 가능하다. 그 경우에는 상속 계층에 있다고 표현한다.
    • 예를들어 우리는 2차원 도형이 많이 필요하다고 상상해보자. 그러면 기억 장소가 많이 필요할 때는 배열을 이용한다. 그런데 우리는 2차원 도형에는 원도 있고 사각형도 있는데 배열의 자료형을 무엇으로 해야할까? 그럴 때 원과 사각형을 모두 담을 수 있는 Shape 으로 배열을 만들 수 있다. (배열의 변수또한 참조 자료형이므로)
    • 단, 자식에게만 있는 메소드를 호출할수는 없다. 따라서 그럴 경우에는 자식 클래스로 형변환해줘야한다.
      • Shape 의 참조변수 ob가 Square를 객체 참조하는데, Square 객체만 m1 메소드를 가지고 있다면
      • ((Square)ob).m1();
      • 이것을 intanceof 로 판별하여 코드를 짤 수 있다.
package com.kosta.exam07;

public class ShapeTest {

	public static void main(String[] args) {
		Square s1 = new Square(0, 0, 10, 20);
		Shape s2 = new Square(-20, 50, 150, 30);
		
		Circle c1 = new Circle(9, 1, 5);
		Shape c2 = new Circle(-19, 184, 3.25);
		
		System.out.println(s1);
		System.out.println(s2);
		System.out.println(c1);
		System.out.println(c2);
	}
}
for(int j = 0; j < n; j++) {
			if(data[j] instanceof TwoDimShape)
				((TwoDimShape)data[j]).calcArea();
			else
				((ThreeDimShape)data[j]).calcVolume();
			System.out.println(data[j]);
		}
  • 2차원 도형도 담고 3차원 도형도 담기 위하여 data 배열의 자료형을 2차원 도형과 3차원 도형 클래스의 부모 클래스인 Shape 로 만들었다. 배열의 요소를 꺼내오면 Shape이다. 만약 그것이 2차원 도형 클래스라면 2차원 도형 클래스로 변환한 후, 면적을 계산시키는 메소드를 호출하고 3차원 도형이라면 3차원 도형클래스로 변환한 후에 부피를 계산하는 메소드를 호출할 수 있다. 

  • 그 배열의 

protected 접근 명시자

  • 부모와 자식이 다른 패키지에 있을 수 있다.(다른 패키지에 있는 것이 더 일반적이다.)
  • 이때 아래와 같이 import 해줘야 한다. 단, 같은 패키지 않에서는 그럴 필요가 없다. 
  • 아무리 상속 관계에 있다고 하더라도, private 영역은 자식 클래스는 접근을 못한다. (생기긴 하지만 접근 불가)
  • private 대신 protected를 사용해서 외부로부터 은닉하지만 상속하게 만들 수 있다.(외부의 다른 클래스로부터는 보호, 자식 클래스에게는 접근 허용)
package com.kosta.exam02;

public class A {
	//private String title;
	//private int year;
	protected String title;
	protected int year;
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public int getYear() {
		return year;
	}
	public void setYear(int year) {
		this.year = year;
	}
	
}
package com.kosta.exam03;

import com.kosta.exam02.A;

public class B extends A {
	private String data;
	
	
	public String getData() {
		return data;
	}


	public void setData(String data) {
		this.data = data;
	}


	public void info() {
		System.out.println(title+", "+year+", "+data);
	}
}
package com.kosta.exam04;

import com.kosta.exam03.B;

public class D03ProtectedTest {

	public static void main(String[] args) {
		B ob = new B();
		
		ob.setTitle("hello");
		ob.setYear(2024);
		ob.setData("Java");
		
		ob.info();
	}

}

 

상속관계와 생성자

  • 상속 관계에 있을 때 부모의 생성자가 먼저 동작된 후 자식의 생성자가 동작된다.
  • 부모의 생성자를 동작시켜달라는 키워드가 super()이다.
  • this()와 마찬가지로 첫 번째 문장에 와야 한다.
  • 자식이 부모의 매개변수들이 있는 생성자를 요구했을때 부모 클래스가 해당 생성자를 갖고 있지 않으면 오류가 뜬다.
  • 재정의(overriding) : 상속 관계에 있는 부모의 속성 등을 자식에 맞게 적는 것.
  • 부모의 속성 또는 메소드를 사용하고 싶을 때는 super 키워드를 사용하면 된다.
    • super()
      • 상속 관계에 있을 때 부모 클래스의 생성자를 실행시키는 키워드.
      • 이 명령은 반드시 생성자의 첫 번째 문장에 와야한다.
    • super
      • super.멤버변수 / super.멤버메소드()
      • 상속 관계에 있을 때 부모 클래스의 멤버와 동일한 이름의 멤버를 자식 클래스에 만들면 부모에 있는 동일한 멤버는 가려지게 되어 자식에게서 만든 멤버가 우선순위가 높다. 그런데 만약 부모클래스에 있는 동일한 이름의 멤버에 접근하려면 super 키워드를 통해서 접근할 수 있다. 
public String toString() {
		return super.toString()+ ", 고객번호: "+no+ ", 마일리지 :"+mileage;
	}
  • overloading vs overriding
    • 메소드 오버로딩(overloading) :
      • 메소드 식별자명 중복 정의 하는 것
      • 매개변수의 개수가 다르거나 자료형이 달라야 한다.
    • 메소드 오버라이딩(overriding) : 상속 관계에 있을 떄 부모의 동일한 이름의 메소드를 자식 클래스에 맞도록 다시 정의(재정의)하는 것을 말하며 메소드 이름 뿐 아니라 매개변수의 개수와 자료형까지 일치되게 재정의해야한다.
      • 어노테이션 @ : at. override 라고 표시해주는  것이다.
  • 추상 클래스와 추상 메소드
    • 미래에 만들어질 후손들이 가져야할 공통적인 속성과 동작을 뽑아서 일반화(부모클래스를 만들 때)할 때 반드시 자식 클래스한테 필요한 메소드인데, 부모 클래스를 만드는 시점에서 body를 구체화 할수 없을 경우 메ㅅ드를 선언만 하게 된다. 이와 같이 body 가 없고 메소드 선언부만 있는 메소드를 "추상메소드"라고 하고 메소드 앞에 abstract 키워드를 붙여준다.
      이렇게 추상 메소드를 1개라도 갖고있는 클래스를 추상클래스라고 하며, 클래스 이름 앞에 abstract 키워드를 붙여줘야 한다.
    • 따라서 어떠한 클래스가 추상 클래스로부터 상속받았다면 반드시 그 추상 클래스 안에 있는 모든 추상 메소드를 오버라이딩 해야한다.
    • 일반적으로 상속관계에 있을 때 자식클래스는 부모의 메소드가 마음에 들면 그대로 사용하고 자신에게 맞지 않으면 선택적으로 메소드를 재정의 (오버라이딩) 할 수 있다. 그런데 만약 자식클래스들에게 어떤 메소드를 반드시 (필수적으로) 오버라이딩 하고자 한다면 그 메소드를 추상메소드로 만들어준다.
    • 추상 클래스는 추상 메소드가 있기 때문에 객체를 생성할 수 없다. 단, 추상 클래스의 참조 변수가 자식 클래스를 참조하는 것은 가능하다.
package com.kosta.exam00;

public abstract class Employee {
	protected String name;
	protected String no;
		
	public Employee(String name, String no) {
		this.name = name;
		this.no = no;
	}

	public Employee() {
		super();
	}

	
	//급여를 계산하는 메소드
	public abstract void computeSalary();
		//월급제 사원과 시간제 사원의 급여 계산 방식이 다르기때문에 부모클래스 만드는 시점에서 메소드의 body 를 구체화할 수 없다. 그래서 비워둔다.
}
  • toString()
    • toString은 애초에 주소를 반환하도록 만들어진 것이지만, 우리가 해당 함수를 쓰는 순간 재정의를 하는 것이다. 따라서 쓸 일이 많으므로 이클립스에서는 getter setter 생성자 함수처럼 기본적으로 쓰는 것을 지원해준다.
  • equals()
    • boolean 타입을 반환한다. 
    • 형식 : 비교할 값1.equals(비교할값);
  • instanceof()
    • 어떤 자료형의 후손인지 물어보는 메소드
    • 상속관계에 있을 때 부모의 참조 변수가 자식의 객체를 참조할 수 있다. 많은 양의 데이터를 처리하기 위하여 배열을 이용하는데, 부모의 참조 자료형으로 배열을 만들면 온갖 종류의 자식 class의 객체를 담을 수 있다. 
    • 그러나 해당 자식 클래스에서 추가되는 메소드를 바로 호출할 수는 없어서 해당 자식 클래스로 형변환해야 하는데, 이 때 어떤 자식 클래스인지 판별할 때 instanceof 연산자를 사용한다.
package com.kosta.exam02;

import java.util.Scanner;

import myutil.Method;
public class ShapeTest {
	public static void main(String[] args) {
		Shape s = new Square(10, 10, 100, 100);
		Shape c = new Cuboid();
		if(s instanceof TwoDimShape)
			System.out.println("2차원 도형입니다.");
		else
			System.out.println("2차원 도형 아님.");
		
		if(c instanceof ThreeDimShape)
			System.out.println("3차원 도형입니다.");
		else
			System.out.println("3차원 도형 아님.");
		
		if(c instanceof Shape)
			System.out.println("도형입니다.");
		else
			System.out.println("도형 아님.");
	}

}

기호상수

  • class 안에서 기호 상수를 쓸 때 static 을 쓰지 않으면 상수인데도 불구하고, 객체마다 생성된다. 따라서 이를 막기 위해 static을 앞에 붙이는 것이 좋다.

'Kosta DevOps 과정 280기 > Java' 카테고리의 다른 글

다형성(polymarphism)  (0) 2024.05.20
접근명시자 정리  (0) 2024.05.20
메소드 중복(Method Overloading)  (0) 2024.05.20
Class-3  (0) 2024.05.17
Class-2  (0) 2024.05.16