Wanna be Brilliant Full-Stack Developer

자바 객체프로그래밍의 이해 오버로딩 본문

Some Memos/Java

자바 객체프로그래밍의 이해 오버로딩

Flashpacker 2023. 3. 2. 16:25


오버로딩이라는것이 무엇인가?

뜻만 보면 OverLoading이라고 하여 오버에서 로딩한다고 하니까 과적재라는 뜻이다.

전에 클래스는 상태와 행위를 가진다고 하였다.

 

이 클래스가 행위를 가지고 있다. 달리기라는 행위를 가지고 있다

그것은 메서드이다.

그러면 내가 동일한 이름에 행위를 만드는것이 불가능하다.

 

근데 오버로딩이라는 기법은 무엇인가하면 어떤 클래스가 행위를 가지고 있을때
이 행위가 이름이 똑같더라도

매개변수 개수가 다르거나 매개변수의 유무나, 매개변수의 타입이 다르거나 

이렇게 다르면 개수가 더 늘어날수도 있다.

 

이렇게 되면 이 4가지 함수 첫번쨰 두번쨰 세번쨰 네번쨰 메서드의 이름이 다 똑같더라도 

우리가 과적재를 하게 되면 과적재하는게 매개변수 개수가 다르고, 타입이 다르고

이렇게되면 다른 함수로 인식을 한다.

 

오버로딩이라는것은 같은 함수의 이름에 똑같아도 다른 함수로 인식하게 하는것을 오버로딩이라고 한다.

함수의 이름이 같아도 다른 함수로 인식하는것!

호출을 어떻게하는가? 

 

1번은 달리기 () 

2번은 달리기(1)

3번은 달리기(1.5)

4번은 달리기(1,5.0)  이렇게 호출을 할 수 있다.

호출을 할때 알아서 동일한 이름이라도 그 메소드를 찾아간다

 

package ch05;

class 임꺽정 {
	
	
	void 달리기() {
		System.out.println("달리기1");
	}
}

public class OOPEx04 {

	public static void main(String[] args) {
		
	}
}

Static이 안붙어 있으니까 new를 해야한다.  Static이 붙어있으면 new를 안해도 되지만

안붙어 있으니까 new를 해야한다

이렇게 달리기 1이 실행이 된다.

이렇게하면 메서드 이름이 똑같으니까 오류가 난다. 

다른함수로 인식하게 하기 위해 오버로딩이라는것을 해보자

package ch05;

class 임꺽정 {
	
	
	void 달리기() {
		System.out.println("달리기1");
	}
	
	void 달리기(int speed) {
		
	}
}

public class OOPEx04 {

	public static void main(String[] args) {
		임꺽정 e = new 임꺽정();
		e.달리기();
	}
}

근데 여기서 e.달리기();는 1번 달리기를 호출한다.

인수가 아무것도 없다. 

 

void 달리기(int speed)

애를 호출하려면 e.달리기( ) 괄호사이에 인수 숫자 1을 넣어야한다.

package ch05;

class 임꺽정 {
	
	
	void 달리기() {
		System.out.println("달리기1");
	}
	
	void 달리기(int speed) {
		System.out.println("달리기2");
	}
}

public class OOPEx04 {

	public static void main(String[] args) {
		임꺽정 e = new 임꺽정();
		e.달리기();
		e.달리기(1);
	}
}

package ch05;

class 임꺽정 {
	
	
	void 달리기() {
		System.out.println("달리기1");
	}
	
	void 달리기(int speed) {
		System.out.println("달리기2");
	}
	
	// 오버로딩
	void 달리기(double speed) {
		System.out.println("달리기3");
	}
}

public class OOPEx04 {

	public static void main(String[] args) {
		임꺽정 e = new 임꺽정();
		e.달리기();
		e.달리기(1);
		e.달리기(5.0);
	}
}

새로운 메소드를 만들어서 매개변수에 double을 넣었으니

밑에 호출하는 인수에도 더블 인수를 넣어서 호출할 수 있다. 

마지막 네번째도 매개변수의 개수가 다르니 다른메소드로 인식을 한다. 

package ch05;

class 임꺽정 {
	
	
	void 달리기() {
		System.out.println("달리기1");
	}
	
	void 달리기(int speed) {
		System.out.println("달리기2");
	}
	
	// 오버로딩
	void 달리기(double speed) {
		System.out.println("달리기3");
	}
	
	void 달리기(int speed, double power) {
		System.out.println("달리기4");
	}
}

public class OOPEx04 {

	public static void main(String[] args) {
		임꺽정 e = new 임꺽정();
		e.달리기();
		e.달리기(1);
		e.달리기(5.0);
		e.달리기(1,5.0);
	}
}

여기서 중요한것은 오버로딩을 왜 사용하는가? 이다.

이렇게 메소드 이름을 아예 다르게도 만들어 달리기 2로 호출할 수도 있다. 

왜 매개변수의 타입이 달라지거나 매개변수가 달라져야하는 오버로딩 기법을 사용하는가? 

 


오버로딩의 한계에 대해서 알아보자!

 

package ch05;

class 전사 { // 검
	String name = "전사";
	
	void 기본공격() {
		System.out.println("검으로 공격하기");
	}
}

class 궁수 { // 활
	String name = "궁수";
	
	void 기본공격() {
		System.out.println("활로 공격하기");
	}
}

class 광전사 { // 도끼
	String name = "광전사";
	
	void 기본공격() {
		System.out.println("도끼로 공격하기");
	}
}



public class OOPEx05 {

	void attack() {
		
	}
	
	public static void main(String[] args) {
		

	}

}

이렇게하면 클래스가 3개가 있는데 전사클래스는 행위가 기본공격이 있고

궁수 클래스가 기본공격이 있고 광전사도 기본공격이 있는데 다 셋다 공격하는 방법이 다르다.

 

그러면 이제 게임을 시작하려고 하는데! 일단 게임이 시작이 되면 이 세가지의 캐릭터가 메모리에 올라가야한다.

package ch05;

class 전사 { // 검
	String name = "전사";
	
	void 기본공격() {
		System.out.println("검으로 공격하기");
	}
}

class 궁수 { // 활
	String name = "궁수";
	
	void 기본공격() {
		System.out.println("활로 공격하기");
	}
}

class 광전사 { // 도끼
	String name = "광전사";
	
	void 기본공격() {
		System.out.println("도끼로 공격하기");
	}
}



public class OOPEx05 {

	void attack() {
		
	}
	
	public static void main(String[] args) {
		전사 u1 = new 전사();
		궁수 u2 = new 궁수();
		광전사 u3 = new 광전사();

	}

}

이제 메모리에 떴으니까 전사가 먼저 공격을 하기 위해서 attack메소드를 사용해보려고 하는데!? 

attack 메소드 앞에 Static을 붙이자!

attack 메소드는 메모리에 떠있다. 

package ch05;

class 전사 { // 검
	String name = "전사";
	
	void 기본공격() {
		System.out.println("검으로 공격하기");
	}
}

class 궁수 { // 활
	String name = "궁수";
	
	void 기본공격() {
		System.out.println("활로 공격하기");
	}
}

class 광전사 { // 도끼
	String name = "광전사";
	
	void 기본공격() {
		System.out.println("도끼로 공격하기");
	}
}



public class OOPEx05 {

	static void attack(전사 u1) {
		u1.기본공격();
	}
	
	public static void main(String[] args) {
		전사 u1 = new 전사();
		궁수 u2 = new 궁수();
		광전사 u3 = new 광전사();
		
		attack(u1);
	}

}

근데 이게 말이 안된다. 공격을 하는데 attack메소드로 하는가 그럴필요가 없다. 

공격을 할꺼면 자기의 행위로 해야한다. 

package ch05;

class 전사 { // 검
	String name = "전사";
	
	void 기본공격() {
		System.out.println("검으로 공격하기");
	}
}

class 궁수 { // 활
	String name = "궁수";
	
	void 기본공격() {
		System.out.println("활로 공격하기");
	}
}

class 광전사 { // 도끼
	String name = "광전사";
	
	void 기본공격() {
		System.out.println("도끼로 공격하기");
	}
}



public class OOPEx05 {

	
	public static void main(String[] args) {
		전사 u1 = new 전사();
		궁수 u2 = new 궁수();
		광전사 u3 = new 광전사();
		
		u1.기본공격();
	}

}

근데 여기서 문제는 누구를 공격하는지가 안나온다. 

일단 전사는 궁수를 공격할것이다. 

package ch05;

class 전사 { // 검
	String name = "전사";
	
	void 기본공격(궁수 e1) {
		System.out.println("검으로 "+e1.name+" 공격하기");
	}
}

class 궁수 { // 활
	String name = "궁수";
	
	void 기본공격() {
		System.out.println("활로 공격하기");
	}
}

class 광전사 { // 도끼
	String name = "광전사";
	
	void 기본공격() {
		System.out.println("도끼로 공격하기");
	}
}



public class OOPEx05 {

	
	public static void main(String[] args) {
		전사 u1 = new 전사();
		궁수 u2 = new 궁수();
		광전사 u3 = new 광전사();
		
		u1.기본공격(u2);
		u2.기본공격();
		u3.기본공격();
	}

}

class 궁수 { // 활
String name = "궁수";

void 기본공격(광전사 e1) {
System.out.println("활로 "+e1.name + " 공격하기");
}
}

이렇게 하면 궁수는 기본공격할때 광전사를 받아야한다.

public class OOPEx05 {


public static void main(String[] args) {
전사 u1 = new 전사();
궁수 u2 = new 궁수();
광전사 u3 = new 광전사();

u1.기본공격(u2);
u2.기본공격(u3);
u3.기본공격();
}

}

 

마지막으로 광전사는 전사를 공격해보자! 

이렇게 만들 수는 있는데 이 프로그램의 단점이 있다. 전사는 궁수만 공격할 수 있고

궁수는 광전사만 공격할 수 있고 광전사는 전사만 공격할 수 있다.

 

그러면 이번에는 전사가 궁수가 아니라 광전사를 공격하고 싶어서

기본공격에다가 u2가 아니라 u3를 넣으려고 하면 안된다. 

광전사 타입이 적합하지 않는다고 오류가 나온다.

u1의 기본공격의 파라미터는 매개변수는 궁수 타입인데 왜 여기에다가 광전사 타입을 넣냐고 물어보는 것이다.

여기에 u2밖에 못넣는것이 단점이다.

그래서 광전사를 공격하기 위해서 복사를 해서 집어 넣어서 기본공격2번이라고 광전사를 넣어보려고 한다.  

 

package ch05;

class 전사 { // 검
	String name = "전사";
	
	void 기본공격(궁수 e1) {
		System.out.println("검으로 "+e1.name+" 공격하기");
	}
	
	void 기본공격2(광전사 e1) {
		System.out.println("검으로 "+e1.name+" 공격하기");
	}
}

class 궁수 { // 활
	String name = "궁수";
	
	void 기본공격(광전사 e1) {
		System.out.println("활로 "+e1.name + " 공격하기");
	}
}

class 광전사 { // 도끼
	String name = "광전사";
	
	void 기본공격(전사 e1) {
		System.out.println("도끼로 "+e1.name+"공격하기");
	}
}



public class OOPEx05 {

	
	public static void main(String[] args) {
		전사 u1 = new 전사();
		궁수 u2 = new 궁수();
		광전사 u3 = new 광전사();
		
		u1.기본공격(u2);
		u2.기본공격(u3);
		u3.기본공격(u1);
		u1.기본공격2(u3);
	}

}

내가 이렇게 되면 공격할떄마다 답답한것이 무엇인가?

이름이 기본공격이 1이 있고 기본공격 2가 있는데 공격해야하는 입장에서 보면 

u1.기본공격은 궁수공격하는거고 u1.기본공격2는 광전사를 공격하는것을 기억해야한다는것이

머리가 굉장히 아프다. 그래서 어떻게 하는게 좋은가? 오버로딩을 사용하면 된다.

이렇게 매개변수 타입만 다르게하게 하면 오버로딩이 된다. 

오버로딩이되면 기억해야하는 메서드 명이 기본공격() 만 기억하면 되어서 편하다. 

내가 유닛이 전사, 궁수, 광전사 밖에 없는데 만약에 유닛이 100개가 넘으면 

함수의 이름이 다달라져야한다. 기본공격1 , 기본공격2, 기본공격3 이걸 다 어떻게 기억하는가?

여기서 오버로딩을 하면 프로그램을 짜는데 굉장히 쉽다.

 

근데 여기서 오버로딩의 단점을 알아보자!

 

package ch05;

class 전사 { // 검
	String name = "전사";
	
	void 기본공격(궁수 e1) {
		System.out.println("검으로 "+e1.name+" 공격하기");
	}
	
	//오버로딩
	void 기본공격(광전사 e1) {
		System.out.println("검으로 "+e1.name+" 공격하기");
	}
}

class 궁수 { // 활
	String name = "궁수";
	
	void 기본공격(광전사 e1) {
		System.out.println("활로 "+e1.name + " 공격하기");
	}
}

class 광전사 { // 도끼
	String name = "광전사";
	
	void 기본공격(전사 e1) {
		System.out.println("도끼로 "+e1.name+"공격하기");
	}
}

class 마법사 { // 마법
	String name = "마법사";
	
	void 기본공격(전사 e1) {
		System.out.println("마법으로 "+e1.name+"공격하기");
	}
}

class 엘프 { // 활
	String name = "엘프";
	
	void 기본공격(전사 e1) {
		System.out.println("엘프로 "+e1.name+"공격하기");
	}
}

class 흑마법사 { // 마법
	String name = "흑마법사";
	
	void 기본공격(전사 e1) {
		System.out.println("흑마법으로 "+e1.name+"공격하기");
	}
}




public class OOPEx05 {

	
	public static void main(String[] args) {
		전사 u1 = new 전사();
		궁수 u2 = new 궁수();
		광전사 u3 = new 광전사();
		
		// 기억해야 할 메서드
		u1.기본공격(u2);
		u2.기본공격(u3);
		u3.기본공격(u1);
		u1.기본공격(u3);
	}

}

 

이렇게 유닛이 늘어나면! 

package ch05;

class 전사 { // 검
	String name = "전사";
	
	void 기본공격(궁수 e1) {
		System.out.println("검으로 "+e1.name+" 공격하기");
	}
	
	//오버로딩
	void 기본공격(광전사 e1) {
		System.out.println("검으로 "+e1.name+" 공격하기");
	}
	
	void 기본공격(마법사 e1) {
		System.out.println("검으로 "+e1.name+" 공격하기");
	}
	
	void 기본공격(엘프 e1) {
		System.out.println("검으로 "+e1.name+" 공격하기");
	}
	
	void 기본공격(흑마법사 e1) {
		System.out.println("검으로 "+e1.name+" 공격하기");
	}
	
	
}

class 궁수 { // 활
	String name = "궁수";
	
	void 기본공격(광전사 e1) {
		System.out.println("활로 "+e1.name + " 공격하기");
	}
}

class 광전사 { // 도끼
	String name = "광전사";
	
	void 기본공격(전사 e1) {
		System.out.println("도끼로 "+e1.name+"공격하기");
	}
}

class 마법사 { // 마법
	String name = "마법사";
	
	void 기본공격(전사 e1) {
		System.out.println("마법으로 "+e1.name+"공격하기");
	}
}

class 엘프 { // 활
	String name = "엘프";
	
	void 기본공격(전사 e1) {
		System.out.println("엘프로 "+e1.name+"공격하기");
	}
}

class 흑마법사 { // 마법
	String name = "흑마법사";
	
	void 기본공격(전사 e1) {
		System.out.println("흑마법으로 "+e1.name+"공격하기");
	}
}




public class OOPEx05 {

	
	public static void main(String[] args) {
		전사 u1 = new 전사();
		궁수 u2 = new 궁수();
		광전사 u3 = new 광전사();
		마법사 u4 = new 마법사();
		엘프 u5 = new 엘프();
		흑마법사 u6 = new 흑마법사();
		
		// 기억해야 할 메서드
		u1.기본공격(u2);
		u2.기본공격(u3);
		u3.기본공격(u1);
		u1.기본공격(u3);
		u1.기본공격(u4);
		u1.기본공격(u5);
		u1.기본공격(u6);
	}

}

이렇게 하면 실행이 잘된다. 근데 단점이 무엇인가?

먼가 개수가 정해지면 오버로딩이 괜찮다. 메서드 이름 하나만 기억하면 되니까!

근데 만약에 유닛이 100개라면 기본공격 메서드가 100개가 되어야한다.

이것은 엄청난 단점이다.

경우의 수가 많으면 많을 수록 메서드가 늘어난다는 단점이 있다. 

 

오버로딩의 장점은 어느정도 개수 및경우의 수가 제한  정해져 있으면 오버로딩이 괜찮다. 

경우의 수가 많으면 힘들어진다.

 

그러면 다음에는 어떻게하면 이렇게 100개의 유닛이 생겼을떄 기본 공격 메서드 한개로 끝낼 수 있는 방법을 배워보자!