[Java] 상속(Inheritance)

당당·2023년 4월 23일
0

Java

목록 보기
18/20

https://opentutorials.org/module/4870

📔설명

클래스와 인스턴스에 대해 공부했으니 그 다음으론 상속(inheritance)에 대해 알아보자.


💰상속이란?

class Cal{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
}

public class InheritanceApp {

	public static void main(String[] args) {
		Cal c=new Cal();
		System.out.println(c.sum(2,1));

	}

}

Cal이라는 클래스를 더이상 수정할 수 없거나, 수정하고 싶지 않을 때를 생각해보자.
근데 저 클래스에 -기능을 넣고 싶다고 생각하자!

가장 쉽게 생각할 수 있는 건 저 클래스 자체를 복사해서, minus라는 새로운 기능을 추가하는 것이다.

class Cal{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
}

class Cal2{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
	public int minus(int v1, int v2) {
		return v1-v2;
	}
}

public class InheritanceApp {

	public static void main(String[] args) {
		Cal c=new Cal();
		System.out.println(c.sum(2,1));

	}

}

이렇게 말이다! 근데 만약 Cal클래스의 sum메소드가 변경이 되었다고 가정해보자.

클래스를 복제했기 때문에 메소드가 변경된지도 모를 뿐더러, 복제한 클래스에도 다 변경사항을 적용해줘야 하는 불편한 상황이 생긴다.

그리고, Cal의 메소드와 Cal2의 메소드 내용이 같은 내용이라는 것을 파악하기도 쉽지 않다. (지금은 한줄이라서 쉽지만..)

이러한 상황에서 행복을 주는 것이 상속이다!

Cal2는 필요하지 않으니 뒤로 보내고, Cal을 상속받은 클래스를 작성해보자.

class Cal3 extends Cal{
	
}

extends를 클래스 이름 뒤에 쓰고, Cal3는 이제, Cal클래스를 확장해서 Cal클래스가 가지고 있는 모든 메소드와 변수를 상속받게 된다.

class Cal{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
}

class Cal3 extends Cal{
	
}

public class InheritanceApp {

	public static void main(String[] args) {
		Cal c=new Cal();
		System.out.println(c.sum(2,1));
		
		Cal3 c3=new Cal3();
		System.out.println(c3.sum(2, 1));
	}

}

class Cal2{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
	public int minus(int v1, int v2) {
		return v1-v2;
	}
}


잘 동작하는 걸 확인할 수있다!
c3가 가리키는 클래스에서 sum이라고 하는 메소드를 찾는데, Cal을 확장하고 있기 때문에 Cal 클래스에서 sum이라는 메소드를 찾는다.

이것이 바로 상속이당


🥇Overriding

위의 소스코드를 더 실용적이게 바꿔보자.
지금 Cal3Cal이 가지고 있는 기능을 그대로 가질 뿐 추가적인게 없다!

Cal이 가지고 있던 기능에서 없던 기능(minus)을 추가할 것이다.`

class Cal{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
}

class Cal3 extends Cal{
	public int minus(int v1, int v2) {
		return v1-v2;
	}
}

public class InheritanceApp {

	public static void main(String[] args) {
		Cal c=new Cal();
		System.out.println(c.sum(2,1));
		
		Cal3 c3=new Cal3();
		System.out.println(c3.sum(2, 1));
		System.out.println(c3.minus(2, 1));
	}

}

class Cal2{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
	public int minus(int v1, int v2) {
		return v1-v2;
	}
}

실행시켜보면 잘 동작한다!

부모 클래스Cal가 가진 sum메소드가 마음에 안들었다고 가정해보자.
수정할 수 있다!!

class Cal{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
}

class Cal3 extends Cal{
	public int sum(int v1, int v2) { //overriding
		System.out.println("Cal3!!");
		return v1+v2;
	}
	
	public int minus(int v1, int v2) {
		return v1-v2;
	}
}

public class InheritanceApp {

	public static void main(String[] args) {
		Cal c=new Cal();
		System.out.println(c.sum(2,1));
		
		Cal3 c3=new Cal3();
		System.out.println(c3.sum(2, 1));
		System.out.println(c3.minus(2, 1));
        System.out.println(c3.sum(2, 1));
	}

}


Cal클래스의 sum이 아닌 Cal3 클래스의 sum으로 동작하는 것을 확인할 수 있다!
이것은, 부모가 가지고 있는 메소드를 재정의 한 것이며, 이것을 Overriding이라고 한다.


👥Overriding vs Overloading

Overriding : 부모 클래스의 기능을 올라타서(덮어써서) 재정의 한 것.
Overloading : 너무 많이 탑재하다. 과적하다. 기존에 존재하는 메소드와 같은 이름의 형태만 다른 메소드.

class Cal{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
	public int sum(int v1, int v2, int v3) { //Overloading
		return v1+v2+v3;
	}
}

Cal 클래스에 형태만 다르고 이름은 같은 메소드를 정의해보았다.

실행시켜보면 자바는 인자 수가 같은 sum(int v1, int v2, int v3)를 찾아서 실행시켜준다는 것을 알 수 있다. 위와 같은 sum 함수를 Overloading이라고 한다.

class Cal{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
}

class Cal3 extends Cal{
	public int sum(int v1, int v2) { //overriding
		System.out.println("Cal3!!");
		return v1+v2;
	}
	
	public int minus(int v1, int v2) {
		return v1-v2;
	}
	public int sum(int v1, int v2, int v3) { //overriding* overloading
		return v1+v2+v3;
	}
}

이렇게 Cal3로 위에서 새로 만든 sum(,,)을 이동시키면, 이 함수는 부모가 없는 기능을 추가하면서, 이름이 같은 것과 함께 공존하는 오버라이딩이자 오버로딩인 것이다.

참고로, 오버로딩은 상속과는 관계가 없다!


🤝🏻this와 super

this는 자기 자신, super은 자신의 부모를 나타낸다.

Cal3sum함수가 부모의 sum함수를 호출하고, 그 결과를 이용할 수 있다면 얼마나 좋을까!

class Cal3 extends Cal{
	public int sum(int v1, int v2) { //overriding
		System.out.println("Cal3!!");
		return super.sum(v1,v2);
	}
	
	public int minus(int v1, int v2) {
		return v1-v2;
	}
	
}

그럴땐 super을 사용하면 된다. 그러면 부모클래스의 sum을 가리킨다!
디버거를 이용해 Step into로 확인해보면 return super.sum을 할 때 부모의 sum 메소드를 호출하는 것을 확인할 수 있다!

this는 자기 자신의 인스턴스를 나타낸다.

class Cal{
	public int sum(int v1, int v2) {
		return v1+v2;
	}
	public int sum(int v1, int v2, int v3) {//overloading
		return this.sum(v1,v2)+v3;//sum(v1,v2)+v3 is ok.
	}
}

자기자신(this)의 sum의 실행시킨 결과이니 위에 있는 sum함수를 호출하게 된다.


🧩상속과 생성자

일단 예제가 복잡해져서 필요없는 것은 지워버리자.

class Cal{
	
}

class Cal3 extends Cal{
	
}

public class InheritanceApp {

	public static void main(String[] args) {
		Cal c=new Cal();
		Cal3 c3=new Cal3();
	}

}

그리고 Cal이라는 클래스에 생성자가 있다면 어떨지를 확인해보자.
처음 생성할 때 Cal(2,3); 으로 생성되도록 해보자.

class Cal{
	int v1,v2;
	Cal(int v1, int v2){
		this.v1=v1;
		this.v2=v2;
	}
}

class Cal3 extends Cal{
	
}

public class InheritanceApp {

	public static void main(String[] args) {
		Cal c=new Cal(2,1);
		Cal3 c3=new Cal3();
	}

}

이렇게 했더니.. Cal3에 빨간 밑줄이 그인다.
Cal을 제대로 계승했다면, Cal이 인스턴스로 만들어질 때 반드시 해야 할 일이 생성자에 들어있다. Cal3도 실행을 시켜야지 제대로 계승하는 것이다.

상속받은 부모가 생성자가 있다면 반드시 자식은 부모 생성자를 실행시키도록 강제하고 있다.

오류 부분에 마우스를 가져다대면 생성자를 만드는 버튼이 있는데, IDE가 알아서 생성자를 생성해준다.

class Cal3 extends Cal{

	Cal3(int v1, int v2) {
		super(v1, v2);
		// TODO Auto-generated constructor stub
	}
	
}

그러면 이렇게 생성자가 알아서 만들어진다.
여기서 super는 부모 클래스를 뜻하고 super(v1,v2)는 부모클래스의 생성자를 뜻한다.

class Cal{
	int v1,v2;
	Cal(int v1, int v2){
		System.out.println("Cal init!!!");
		this.v1=v1;
		this.v2=v2;
	}
}

class Cal3 extends Cal{

	Cal3(int v1, int v2) {
		super(v1, v2);
		System.out.println("Cal3 init!!");
	}
	
}

public class InheritanceApp {

	public static void main(String[] args) {
		Cal c=new Cal(2,1);
		Cal3 c3=new Cal3(2,1);
	}

}

이 소스코드를 실행해보자!

처음 Cal인스턴스를 생성했을 때는 Cal클래스의 생성자만 호출되지만, Cal3의 인스턴스를 생성했을 때는 부모클래스의 생성자와 자기 자신의 생성자를 호출한다!

아까 없앴던 sum함수와 minus를 다시 도입하고, 계산 결과를 확인해보자!

class Cal{
	int v1,v2;
	Cal(int v1, int v2){
		System.out.println("Cal init!!!");
		this.v1=v1;
		this.v2=v2;
	}
	public int sum() {
		return this.v1+this.v2;
	}
}

class Cal3 extends Cal{

	Cal3(int v1, int v2) {
		super(v1, v2);
		System.out.println("Cal3 init!!");
	}
	public int minus() {
		return this.v1-this.v2;
	}
}

public class InheritanceApp {

	public static void main(String[] args) {
		Cal c=new Cal(2,1);
		Cal3 c3=new Cal3(2,1);
		System.out.println(c3.sum()); //3
		System.out.println(c3.minus()); //1
	}

}

더이상 sum함수와 minus함수에 인자가 필요가 없게 된다.


🕦이후

다형성(Polymorphism) : 자식클래스를 부모클래스로 동작하도록 규제하는 기술

ParentClass obj=new ChildClass()

Final 다른 사람이 더이상 상속하지 못하거나 오버라이딩 하지 못하게, 변수를 마음대로 수정하지 못하게 만든다.

Abstract 클래스를 상속해서 사용하려는 사용자에게 어떤 특정한 메소드는 꼭 구현하라고 강조하고 싶을 때 사용한다.

보아야 할 강의가 엄청 많았다..
왜 저번에 적다고 생각했지?!

아무튼 다음은 자바의 인터페이스에 대해서 알아보자!

profile
MySQL DBA 신입 지원

0개의 댓글