본문 바로가기
개발 공부 Today I Learned

[국비 21일차 TIL] 자바 오버라이드, 오버로드, 다형성, 추상화

by 개발자신입 2023. 12. 19.
반응형


고양이 만들기

System.out.println("\\    /\\\n )  ( ')\n(  /  )\n \\(__)|");



강아지 만들기

System.out.print("|\\_/|\n|q p|   /}\n( 0 )\"\"\"\\\n|\"^\"`    |\n||_/=\\\\__|");

 

 

- 상속 + 접근제어자 protected

1. 코드 줄이기
2. 클래스간 관계 생성하여 활용하기 = 다형성

 

package com.potato.inheritance;

class Animal {			// 중복 코드를 한 곳으로 취합
	String name;
	int age;
	public void sleep() {}
}

class Cat extends Animal {}
class Dog extends Animal {}


public class Inheritance {

	public static void main(String[] args) {
		
		Cat cat = new Cat();
		Dog dog = new Dog();
		Animal animal = new Animal(); 	// 추상화 -> 인터페이스
		
		cat.sleep();
		cat.name = "kong";
	}
}

 

 

< 다형성 >

다형성( polymorphism )은 동적 바인딩이 지원되는 프로그램 언어에서 사용 가능함.

 

런타임때 최종 타입이 결정됨.

 

하나의 객체가 여러가지 형태를 가질 수 있는 것

 

자바의 다형성은 한 타입의 참조변수를 통해 여러 타입의 객체를 참조할 수 있도록 함.

상위 클래스 타입의 참조변수를 통해 하위 클래스의 객체를 참조할 수 있도록 허용하여, 상위 클래스가 동일한 메시지로 하위 클래스들이 서로 다른 동작을 할 수 있게 하는 것.

 

다형성을 활용하면 부모 클래스가 자식 클래스의 동작 방식을 알 수 없어도 오버라이딩을 통해 자식 클래스에 접근할 수 있음.

 

> 장점

  • 유지보수 : 여러 객체를 하나의 타입으로 관리할 수 있음.
  • 재사용성 : 객체의 재사용이 쉬워짐.
  • 느슨한 결합 : 클래스간의 의존성을 줄여, 확장성은 높아지고 결합도는 낮아짐.

 

> 다형성의 조건

  • 상위 클래스와 하위 클래스는 상속관계여야 함.
  • 다형성이 보장되기 위해서 오버라이딩이 반드시 필요함.
  • 자식 클래스의 객체가 부모 클래스의 타입으로 형변환 해야 한다.
class Hero {
	String name;
	void attack() {
		System.out.println("공격");
	}
}

class Ironman extends Hero {
	public void makeSuit() {
		System.out.println("javis 수트 만들어.");
	}
	
	@Override
	void attack() {
		System.out.println("레이저 공격");
	}
}

class Hulk extends Hero {
	@Override
	void attack() {
		System.out.println("주먹 공격");
	}
}

class Captain extends Hero {
}

class Superman extends Hero {
}

public class PolyMorphism {
	public static void main(String[] args) {
		
		Ironman iron = new Ironman();
		Hero h = new Ironman();		// 반드시 부모 클래스가 앞에 나와야 함.

//		h는 실제로 Ironman 클래스의 인스턴스를 참조하고 있지만, 
//		부모 클래스인 Hero 타입으로 선언
		
		iron.makeSuit();			// Hero가 가지고 있는 부분만 사용 가능.
//		h.makeSuit();				
		
		Ironman mark2 = (Ironman) h;
		mark2.makeSuit();
		
		((Ironman)h).makeSuit();
		
//		Hulk hulk = (Hulk)h;
//		hulk.makeSuit();				// hulk에는 없는 기능임.
		
		Hulk hulk = new Hulk();
		hulk.attack();
		
//		((Ironman)hulk).makeSuit();
//		((Ironman)((hero)hulk)).makeSuit();
		
		Hero avengers[] = new Hero[4];
		avengers[0] = new Ironman();
		avengers[1] = new Hulk();
		avengers[2] = new Captain();
		avengers[3] = new Superman();
		
		for (Hero hero : avengers) {
			hero.attack();
		}
	}
}

 

오버라이드 & 오버로드 

오버라이드 = 부모 클래스의 메서드를 자식 클래스에서 다시 정의하는 것
오버로드     = 같은 이름의 다른 메서드를 정의하는 것

 

 

< 오버라이드 >

메소드 오버라이드는 객체지향 언어에서 자주 사용되는 기술임.

상속받은 부모 메소드의 내용을 자식 클래스에서 재정의하여 자식 내용에 맞게 바꿔 사용하는 것.

 

주의사항

  • 메소드의 형식은 상속받은 메소드와 동일해야 함.
  • 접근 제어자는 반드시 부모의 것과 동일하거나, 더 큰 범위여야 함.
  • 재정의된 메소드의 부모 메소드는 자식의 것에 은닉(hide)되어지기 때문에 직접적으로는 더이상 호출 불가함.
  • 은닉 된 부모의 메소드를 호출할 때는 [ [ super ] ]라는 키워드를 통해 호출.

 

< 오버로드 >

메소드는 클래스에서 특정 기능을 수행하도록 설계되어 있음.

정의 시 컴파일러는 같은 메소드로 중복 정의가 되어있는지 확인함.

확인 시 기준 요소로는 메소드 이름과 메소드 파라미터가 있음.

이 중 파라미터는 3가지로 나뉨 :

- [ [ 파라미터의 수, 순서, 타입 ] ]으로 구분함. - 3가지 요소를 메소드 시그니쳐라고 부름.

 

만약 정의된 메소드가 이름과 시그니쳐까지 같다면 그것은 같은 메소드로 인식하여 컴파일 에러 발생.

시그니쳐 중 하나라도 다르면 다른 메소드로 인식함.

따라서 필요시 같은 이름의 메소드를 여러 개 정의 할 수 있음.

, 시그니쳐는 다르게 해야함.

 

이렇게 같은 이름의 메소드를 시그니쳐만 다르게 해서 정의하는 것 메소드 [ [ 오버로드/오버로딩 ] ]이라고 함.

 

호출 시에는 반드시 호출하려는 메소드의 시그니쳐에 맞게 호출해야함.

 

 

class Animal extends Test03 {			// 중복 코드를 한 곳으로 취합
	protected String name; 		// protected 추가
	private int age;			// private 추가
	public void sleep() {
		testNumber = 10;
	}
}

class Cat extends Animal {
	protected int catNumber;	// 패키지 뛰어넘기 가능
	public void change() {
		testNumber = 10;
	}
	
	@Override	// 어노테이션 = 표시 기능 
	public void sleep() {
//		super.sleep();		// 상위 클래스의 sleep 메서드 호출
		System.out.println("고양이가 침대에서 잠을 잡니다.");
	}
}

class Dog extends Animal {

	@Override
	public void sleep() {
		System.out.println(name + "이 집에서 잠을 잡니다.");
	}
}

public class Inheritance extends Test03 {

	public static void main(String[] args) {
		
		Cat cat = new Cat();
		Dog dog = new Dog();
		Animal animal = new Animal(); 	// 추상화 -> 인터페이스
		
		cat.sleep();
		cat.name = "kong";
		
		Inheritance inheritance = new Inheritance(); // 상속받은 개체 생성
		System.out.println(inheritance.testNumber);
		
		cat.catNumber=5;	 // cat에서 protected를 설정함.
		
		cat.sleep();
		dog.sleep();
	}
}

 

 

< 추상화 >

추상(abstract) : 현실화 되어질 필요가 없는 인스턴스화 되어질 필요가 없는 클래스 ---> 객체 생성 불가

 

추상이라는 개념을 클래스에 적용시키면 자신의 인스턴스를 발생할 수 없는 상태로 지정됨.

 

인스턴스화 할 필요가 없지만 상속 개념에서 중요한 위치를 가지는 클래스를 보통 추상 클래스로 정의함. (상속 용도로만 쓰도록, super클래스로만 존재하도록)

 

강제적임.

 

선언규칙

1. 클래스에 정의된 메소드 중 추상 메소드가 하나라도 있다면 해당 클래스는 무조건 추상 클래스가 됨.

추상 클래스는 class 앞에 abstract 라고 적어야 함.

 

2. 추상 메소드는 메소드 바디가 없는 형태.

abstract 라는 키워드를 리턴타입 앞에 선언함. (void )

파라미터 괄호() 뒤에 세미콜론; 붙여서 명령을 끝냄.

 

3. 추상 클래스는 자신의 인스턴스를 발생시키지 못함.

but 생성자, 메소드, 필드는 모두 선언 및 정의할 수 있음. 상속도 가능.

또한 super type으로 존재 가능하므로 다형성도 적용됨.

 

4. 추상 클래스가 되기 위해 class 앞에 abstract 만 붙이면 됨.

 

 

추상 클래스

: 인터페이스의 역할도 하면서 클래스같은 돌연변이 클래스

 

추상 클래스는 class 앞에 abstract를 붙인다.

내부 메소드에도 abstract를 붙이고 바디를 제거.

인터페이스와 동일하게 메소드 바디{}가 없음.

- 상속받은 자식 클래스에서 미구현된 메소드를 구현해줘야 함.

 

추상 클래스 : 객체를 만들 수 없는 클래스

- 미완성된 기능이 있기 때문에 객체 생성 불가.

 

추상 메소드 : 바디가 없는 메소드 (내용이 없음)

미완성된, 구현이 없는 메소드는 하위 클래스에서 구현함 (오버라이드)

하위 클래스에서 강제로 구현 시킴. 프로그램 구조가 확실해짐.

 

 

package abstractEx;

abstract class Hero {
	String name;
	int age;
	
	public abstract void attack();		// 추상 메소드 (리턴 타입 앞에 abstract)
	public abstract void sleep();
	void eat() {
		
	}		// 바디가 있다는 것은 정상적인 메소드임.
}

class Ironman extends Hero {
	@Override
	public void attack() {
		System.out.println("레이저 공격");
	}

	@Override
	public void sleep() {
		
	}
}

class Hulk extends Hero {
	@Override
	public void attack() {
		System.out.println("몸통 박치기");
	}

	@Override
	public void sleep() {
		
	}
}

class Spiderman extends Hero {

	@Override
	public void attack() { 	// 미구현된 메소드를 자식 클래스에서 구현		
	}

	@Override
	public void sleep() {
		
	}
}

class Hawkeye extends Hero {

	@Override
	public void attack() {		// {} (바디)가 있다면 구현한 것으로 인정
	}

	@Override
	public void sleep() {
		
	}
}

public class AbstractEx {

	public static void main(String[] args) {
		
//		Hero hero = new Hero();		// 위의 Hero가 추상화되었기 때문에 인스턴스 생성 불가
		
		Hero hero = new Spiderman();	
	}
}

정보처리기사 문제

package gisa;

public class Test01 {
	public static void main(String[] args) {
		
		A b = new B(); // B를 A에 넣음
		b.paint();
		b.draw();
	}
}

class A {
	public void paint() {
		System.out.print("A");
		draw();
	}

	public void draw() {
		System.out.print("B");  // (1) B출력
		draw(); // draw 반복 : 맨 아래 draw로 이동 = (2) D 출력
	}
}

class B extends A {
	public void paint() {
		super.draw();			// super = A, A의 draw로 감.
		System.out.print("C");
		this.draw();	// (3) C 출력
	}

	public void draw() {
		System.out.print("D");	// (4) D 출력, 맨 처음 draw로 가서 다시 여기로 내려옴. (5) D출력
	}
}

 

A b = new B();

B 클래스의 인스턴스를 생성하고, 이를 A 타입의 변수 b에 할당합니다.
b.paint();

A 클래스의 paint 메서드가 호출됩니다.
paint 메서드 내에서 "A"를 출력하고, draw 메서드를 호출합니다.
b.draw(); (실제로 호출되는 메서드는 B 클래스의 draw 메서드)

B 클래스의 draw 메서드가 호출됩니다.
(4)에서 "D"를 출력합니다.
super.draw(); (부모 클래스인 A의 draw 메서드 호출)

A 클래스의 draw 메서드가 호출됩니다.
(1)에서 "B"를 출력하고, 다시 draw 메서드를 호출합니다.
(2)에서 "D"를 출력합니다.
this.draw(); (현재 클래스인 B의 draw 메서드 호출)

B 클래스의 draw 메서드가 호출됩니다. (4)에서 "D"를 출력합니다.

 


정보처리기사 오류나는 라인 찾기

 

package gisa;

class Person {
	private String name;

	public Person(String val) {
		name = val;
	}

	public static String get() { // static이 붙어있어서
		return name;		// 오류나는 부분 라인11
	}

	public void print() {
		System.out.println(name);
	}
}

public class Test03 {
	public static void main(String[] args) {
		Person obj = new Person("Kim");
		obj.print();
	}
}
반응형

댓글