230724

객체지향 프로그래밍 - 다형성, 추상 클래스

🌍TISTORY

  • 다형성
  • 추상클래스
  • 추가) 바인딩 - 동적 바인딩, 정적 바인딩

다형성


다형성의 모습

다형성 0

`public class Person {
	String name;
	int age;
	Person() {}
	Person(String name) {
		this.name = name;
	}
	public void eat () {
		System.out.println("음식을 먹는다.");
	}
}
public class Student extends Person {
	String major;
	public void study() {
		System.out.println("공부를 한다.");
	}
}
  • 다형성이란 多形을 가질 수 있는 성질
  • 상속관계에 있을 때 조상 클래스의 타입으로 자식 클래스 객체를 참조할 수 있다

다형성 1

다형성 2

메모리 상의 모습

= new Student

다형성 3

참조변수의 형 변환

다형성 4

  • 자손타입 → 조상타입 (묵시적 형 변환)
  • 형 변환 생략 가능
Student st = new Student();
Person p = st;

다형성 5

Person p = new Student();
Student st = (Student) p;

참조변수의 형 변환

다형성 6

Person p = new Person();
Student st = (Student) p;

다형성 7

다형성 8

instanceof 연산자

다형성 9

  • 참조변수가 참조하고 있는 인스턴스의 타입을 확인하기 위해 사용
  • 결과를 boolean으로 반환
  • true가 반환되면 해당 타입으로 형 변환 가능
Person perseon = new Person();

if (person instanceof Student) {
	Student student = (Studnet) person;
	student.study();
}

참조 변수와 인스턴스 멤버의 관계

class Parent {
	String x = "parent";
	public Stirng method() {
		return "parent method!!!!";
	}
}

class Child extends Parne {
	String x = "child";

	@Override
	public String method() {
		return "child method!!!!";
	}
}
public class BindingTest {
	public static void main(String[] args) {
		Parent p = new Child();
		Child c = new Child();

		System.out.println(p.x);
		System.out.println(p.method());

		System.out.println(c.x);
		System.out.println(c.method());
	}
}
  • 상속관계에서 멤버 변수가 중복되면 참조 타입에 따라 연결이 달라짐
  • 메서드가 중복될 때 무조건 자식 클래스의 메서드가 호출됨
  • static 메서드는 참조변수 타입의 영향을 받기 때문에 이를 방지하고자 클래스 이름으로 메서드 호출
  • 다형성의 활용 - 다른 타입의 객체를 다루는 배열

다형성 10

다형성 11

다형성 12

다형성의 활용 - 매개변수의 다형성

  • 사람의 정보를 출력하려면
    public String info(Teacher t) { }
    public String info(Student st) { }
    

    public String info (Person p) {
    	String s = String.valueOf(x);
    	synchronized (this) {
    		print(s);
    		newLine();
    	}
    }
    

동적 바인딩… 공부하기

추상클래스 (abstract class)


추상클래스

상속을 강제하기 위한 것

메소드의 시그니처만 정의해 놓고 그 메소드의 실제 동작 방법은 이 메소드를 상속받은 하위 클래스의 책임으로 위임한다.

다형성 13

추상 클래스 정의

다형성 14

public class KFoodChef {
	String name;
	int age;
	String speciality;

	public void eat(){
		System.out.println("음식을 먹는다.");
	}

	public void cook() {
		System.out.println("한식을 요리한다.");
	}
}

public class JFoodChef {
	String name;
	int age;
	String speciality;

	public void eat() {
		System.out.println("음식을 먹는다.");
	}

	public void cook() {
		System.out.println("일식을 요리한다.");
	}
}

추상 클래스 정의

다형성 15

public class Chef {
	String name;
	int age;
	String speciality;
	public void eat() {
		System.out.println("음식을 먹는다.");
	}
	public void cook() {
		System.out.println("전공에 맞는 요리한다.");
	}
}

public class KFoodChef extends Chef{
@Override
	public void cook() {
		System.out.println(한식을 요리한다.");
	}
}

public class JFoodChef extends Chef{
 @Override
	public void cook() {
		System.out.println("일식을 요리한다.");
	}
}
public class ChefTest {
	public static void main(String[] args) {
		Chef[] chefs = new Chef[2];
		chefs[0] = new KFoodChef();
		chefs[1] = new JFoodChef();
		for(Chef chef : chefs) {
			chef.eat();
			chef.cook();
		}
	}
}
  • 한식요리사, 일식요리사 모두 cook 메서드를 가지고 있음.
  • 조상 클래스 Chef에 선언하고 각 자손 클래스에서 override 예정
  • 사용되지 않는 Chef 클래스에 cook() 메서드가 필요한가? → 요리사라는 객체를 만들 때 전공을 무조건 선택하게 한다면 **= 자손 클래스에서 무조건 재정의** Chef (cook()) 메서드는 필요 없지 않나?
public class Chef {
	String name;
	int age;
	String speiciality;

	public void eat() {
		System.out.println("음식을 먹는다.");
	}

	public void cook() {
		System.out.println("전공에 맞는 요리한다.");
	}
}
for (Chef chef: chefs) {
	chef.eat();
	if (chef instanceof KFoodChef) {
		KFoodChef k = (KFoodChef) chef;
		k.cook();
	} else if (chef instanceof JFoodChef) {
		((JFoodChef) chef).cook();
	}
}

다형성 16

  • cook() 메서드는 자손 클래스에서 반드시 재정의해서 사용되기 때문에 조상의 구현이 무의미
  • 하지만? “전공에 맞는 요리를 한다” 를 삭제했는데, 모두가 cook 메서드를 가지고 있지만 cook 메서드에 대한 강제성이 없음! cook 메서드가 없는 클래스가 존재할 수도 있음
  • 메서드의 선언부만 남기고 구현부는 ;(세미콜론)으로 대체 ::: 미완성 method 미완성 method 자식 클래스에서 무조건 재정의해야 한다는 것을 의미 → 자식 클래스에서 재정의하지 않으면 빨간 줄 나옴~ → Chef 클래스도 미완성 클래스가 됨
  • 구현부가 없으므로 abstract 키워드를 메서드 선언부에 추가
  • 객체를 생성할 수 없는 클래스라는 의미로 클래스 선언부에 abstract를 추가
public abstract class Chef {
	String name;
	int age;
	String speciality;

	public void eat() {
		System.out.println("음식을 먹는다.");
	}

	public abstract void cook();
}

추상 클래스 특징

  • abstract 클래스는 상속 전용의 클래스 → 인스턴스 생성 불가
  • 클래스에 구현부가 없는 메서드가 있으므로 객체를 생성할 수 없음
  • 상위 클래스 타입으로 자식을 참조할 수는 없음
    //생성할 수 없음
    Chef chef1 = new Chef();
    Chef chef2 = new KFoodChef();
    
  • 조상 클래스에서 상속받은 abstract 메서드를 재정의하지 않은 경우 클래스 내부에 abstract 메서드가 있으므로 자식 클래스는 abstract 클래스가 되어야 함

∴ 추상 클래스 사용하는 이유?

  • abstract 클래스는 구현의 강제를 통해 프로그램의 안정성 향상
  • 부모가 구현하고 싶은 내용이 없다고 해서 구현하지 않는다면, 동적 바인딩에 의해 자식의 오버라이딩된 함수가 실행되지 않음!!
  • 아무것도 작성하지 않은 {} 를 만들어 놓으면 자식이 구현하든, 하지 않든 상관없음
  • abstract로 만들어 두면 무조건 구현해야 하는 의무를 부여함

### 정리

추상클래스는 미완성의 설계도: 인스턴스를 생성할 수 없음

인스턴스를 생성할 때 미완성된 부분을 만들어 주면 됨!

익명 클래스 문법을 이용해서 1회 한정으로 구현하고 인스턴스를 만들 수 있게 도와준다

Chef c2 = new Chef() {
	public void cook () {
		System.out.println("아무요리나다해버림ㅎㅎ");
	}
}

Binding 바인딩


바인딩이란?

  • Association of method call to the method body: 메서드 호출과 메서드 본문의 연결
  • 컴퓨터 프로그래밍에서 각종 값들이 확정되어 더이상 변경할 수 없는 구속(bind) 상태가 되는 것
  • 프로그램 내에서 식별자(identifier)가 그 대상인 메모리 주소, 데이터형 또는 실제 값으로 배정되는 것

Static Binding 정적 바인딩

  • 컴파일링 또는 링크 시에 확정되는 바인딩을 의미
  • 실행 이전에 값이 확정되면 → 정적 바인딩
  • static private final method: override 불가능하며 compile 시점에 타입이 결정된다.

Dynamic Binding 동적 바인딩

  • 프로그램이 실행되는 과정에서 바인딩되는 것을 의미
  • method overriding: 부모와 자식 클래스에 모두 동일한 메서드가 존재하고 오직 실행 시점에 타입이 결정되어 해당 타입의 method가 실행됨