티스토리 뷰
목차
📍 동적 바인딩
– 상속 관계에서 업캐스팅 후 메서드를 호출할 때,
컴파일(번역) 단계에서의 실행 대상과 실제 실행 단계에서의 실행 대상이 달라진 것.
– 참조변수의 타입과 무관하게 '실제 인스턴스'의 메서드를 실행하게 됨.
– 업캐스팅 시 일어나는 동적바인딩은 ctrl + click으로 해당 메서드의 출처를 알기가 힘듬.
원래라면 참조하고 있던 메모리가 없어졌으므로 에러가 나야하는데 Java가 에러 안나게끔 길을 바꿔준 것.
EX ) |
• 클래스 생성하기
class Parent{ public void parentPrn() { System.out.println("슈퍼클래스의 parentPrn()"); } } class Child extends Parent{ public void childCrn() { System.out.println("서브클래스의 childCrn()"); } @Override public void parentPrn() { System.out.println("서브클래스에서 오버라이딩 된 parentPrn()"); } }
– 슈퍼클래스 Parent와 이를 상속받는 Child클래스를 생성하고 각각 parentPrn(), childCrn() 메서드를 정의함.
– 서브클래스에 슈퍼클래스의 parentPrn() 메서드를 오버라이딩 함.
• 슈퍼클래스 타입 인스턴스 생성
public static void main(String[] args) { Parent p = new Parent(); p.parentPrn();
✓ 실행 결과
슈퍼클래스의 parentPrn( )
• 서브클래스 타입 인스턴스 생성
Child c = new Child(); c.childCrn(); // 서브클래스에서 정의한 메서드 c.parentPrn(); // 슈퍼클래스로부터 상속받은 메서드
– 슈퍼클래스로부터 상속받은 메서드를 서브클래스에서 재정의 (오버라이딩)하면
기본 슈퍼클래스의 메서드와 동일하게 생긴 메서드를 정의하게 되므로,
서브클래스에서는 더 이상 슈퍼클래스의 메서드가 보이지 않음.
→ 슈퍼클래스의 메서드를 덮어쓰기 때문에 자신의 메서드만 보이게 됨. (슈퍼클래스 메서드는 은닉됨)
→ 따라서 슈퍼클래스에서 정의한 parentPrn() 메서드가 호출되지 않고,
서브클래스에서 오버라이딩한 parentPrn()이 호출되어 해당 출력문이 출력됨.
✓ 실행 결과
서브클래스의 childCrn( )
서브클래스에서 오버라이딩 된 parentPrn( )
• 업캐스팅
p = c; // Parent p = new Child();
– 서브클래스 타입 인스턴스 → 슈퍼클래스 타입으로 업캐스팅 (Child → Parent)
→ 자식클래스 타입에서 부모클래스 타입으로 가는 것
– Parent p가 Child c를 참조하므로 p가 참조하던 Heap영역의 인스턴스는 GC(Garbeage Collection)에 의해 제거됨.
p.parentPrn(); } // main() 메서드
– Child 인스턴스의 오버라이딩 된 메서드가 호출됨.
– 메서드 호출 코드를 작성하는 시점 (컴파일 시점)에서는
참조변수 타입인 Parent 클래스의 parentPrn() 메서드를 호출하는 코드지만,
실제 실행하는 시점에서 참조변수에 저장된 인스턴스가 Child의 인스턴스이므로,
실제 호출되는 메서드는 Child 타입의 오버라이딩 된 메서드가 호출됨.
→ 즉, 컴파일 단계에서의 실행 대상과 실행 단계에서의 실행 대상이 다를 수 있음.
✓ 실행 결과
서브클래스에서 오버라이딩 된 parentPrn()
📍 다형성 (polymorphism)
– 하나의 데이터타입 (참조변수)로 여러 인스턴스를 참조하는 특성.
– 어떤 인스턴스를 업캐스팅하여 슈퍼클래스 타입 변수로 다루면,
하나의 슈퍼클래스 타입으로 여러 서브클래스 타입 인스턴스를 다룰 수 있음.
EX ) |
• 슈퍼클래스 Shape 정의
class Shape{ public void draw() { System.out.println("도형 그리기"); } }
– 여러 도형의 공통점인 '그리다' 기능을 수행하는 drwa() 메서드 정의.
• 서브클래스 정의
class Circle extends Shape{ @Override // alt + shift + s + v public void draw() { System.out.println("원 그리기"); } public void CirclePaint() { System.out.println("원 그리기"); } }
– draw() 메서드를 오버라이딩 하여 "원 그리기" 출력.
→ 하나하나 메서드를 따로 지정하면 유지보수도 안좋고 나중에 내가 어떤걸 써야 할 지 헷갈림.
class Rectangle extends Shape{ @Override public void draw() { System.out.println("사각형 그리기"); } public void rDraw() { System.out.println("사각형 그리기"); } }
class Triangle extends Shape{ @Override public void draw() { System.out.println("삼각형 그리기"); } public void design() { System.out.println("삼각형 그리기"); } }
– Shape 클래스를 상속받는 Circle, Rectangle, Triangle 서브 클래스를 정의하고,
Shpae 클래스의 draw() 메서드를 각 서브클래스에서 오버라이딩 함.
→ 코드의 유지보수와 통일성이 향상됨.
• 메서드 오버라이딩 하지 않았을 경우
public static void main(String[] args) { // Circle 인스턴스 생성 Circle c = new Circle(); c.CirclePaint(); // Rectangle 인스턴스 생성 Rectangle r = new Rectangle(); r.rDraw(); // Triangle 인스턴스 생성 Triangle t = new Triangle(); t.design();
– 클래스별로 똑같은 기능을 하는 메서드라도 이름이 다르므로 일일이 기억해야하는 번거로움이 있음.
✓ 실행 결과
원 그리기
사각형 그리기
삼각형 그리기
• 슈퍼클래스의 메서드를 상속받아 오버라이딩 하는 경우
c.draw(); r.draw(); t.draw();
– 각 참조변수에 같은 이름과 기능을 하는 오버라이딩 된 메서드 하나만 호출하면 되므로 코드의 통일성이 향상됨.
✓ 실행 결과
원 그리기
사각형 그리기
삼각형 그리기
• 업캐스팅
– 업캐스팅을 활용하면 코드의 통일성을 더욱 더 향상시킬 수 있음.
– Circle, Rectangle, Triangle의 공통 슈퍼클래스인 Shape타입 (s)으로 세 인스턴스를 컨트롤 가능함.
// Circle → Shape 업캐스팅 Shape s = new Circle(); s.draw();
– Circle 인스턴스를 Shape타입의 참조변수 s에 저장 (업캐스팅)하여 Circle 인스턴스의 draw() 메서드를 호출함.
// Rectangle → Shape 업캐스팅 s = new Rectangle(); s.draw(); // Triangle → Shape 업캐스팅 s = new Triangle(); s.draw();
– 마찬가지로 Shape타입의 참조변수 s에 Rectangle 인스턴스와 Triangle 인스턴스를 각각 저장 (업캐스팅)하여,
해당 인스턴스들의 draw() 메서드를 호출함.
✓ 실행 결과
원 그리기
사각형 그리기
삼각형 그리기
• 배열에 다형성 적용하기
1. 저장
방법1 ) 하나씩 저장하기
// Shape타입 배열 3개 생성 Shape[] sArr = new Shape[3];
– 다형성을 배열에 적용시키는 경우.
– 슈퍼클래스 타입으로 배열을 생성하여, 배열의 인덱스에 각각의 서브클래스 인스턴스 저장 가능.
sArr[0] = new Circle();
– Circle → Shape 으로 업캐스팅.
– 0번 인덱스에 Circle 인스턴스 생성하여 저장함.
sArr[1] = new Rectangle();
– Rectangle → Shape 으로 업캐스팅.
– 1번 인덱스에 Rectangle 인스턴스 생성하여 저장함.
sArr[2] = new Triangle();
– Triangle → Shape 으로 업캐스팅.
– 2번 인덱스에 Triangle 인스턴스 생성하여 저장함.
방법2 ) 한번에 저장하기
Shape[] sArr = {new Circle(), new Rectangle(), new Triangle()};
– 슈퍼클래스 타입으로 배열을 생성하여 문법에 맞게 데이터를 입력해야 할 곳에 각 서브클래스의 인스턴스를 입력함.
2. 배열 호출
방법1 ) 인덱스 사용
sArr[0].draw(); sArr[1].draw(); sArr[2].draw();
– 배열의 각 인덱스에는 인스턴스 주소가 저장되므로,
참조변수와 마찬가지로 배열명[인덱스].메서드명() 형태로 호출 가능함.
✓ 실행 결과
원 그리기
사각형 그리기
삼각형 그리기
방법2 ) 반복문 사용
for(int i=0; i<sArr.length; i++) { sArr[i].draw(); }
– 반복문을 사용하여 Shape 배열 크기만큼 반복하면서 각 인덱스에 저장된 인스턴스의 draw() 메서드 호출.
✓ 실행 결과
원 그리기
사각형 그리기
삼각형 그리기
📍 메서드에 다형성 활용
1. 이미 다형성이 적용된 배열을 메서드 파라미터로 전달
2. 메서드 파라미터로 인스턴스를 전달
EX ) |
• static 메서드 정의하기
} // main 메서드 끝 public static void polymorphismDraw(Shape[] sArr) { for(int i=0; i < sArr.length; i++) { sArr[i].draw(); } } public static void polymorphismDraw2(Shape s) { s.draw(); } } // Ex2 클래스 끝
– main 메서드 아래 부분에 static 메서드를 정의함.
– 이미 다형성이 적용된 배열 (Shape s)을 polymorphismDraw() 메서드의 파라미터로 전달.
→ Shape 배열 타입으로 줘야하므로 해당 메서드 파라미터 부분에 'Shape[] Arr'를 입력함.
– polymorphismDraw2() 메서드에는 어떤 인스턴스가 전달되더라도 draw() 메서드는 공통으로 호출 가능함.
→ 파라미터로 정의된 Shape타입의 참조변수 s에 Circle, Rectangle, Triangle타입의 인스턴스를 각각 전달하면
s에 해당 인스턴스가 저장되고, 각 서브클래스에서 오버라이딩 된 draw() 메서드가 호출됨.
• main 메서드에서 호출하기
// 이미 다형성이 적용된 배열을 메서드 파라미터로 전달 polymorphismDraw(sArr); // 메서드 파라미터로 각 인스턴스를 전달 polymorphismDraw2(new Circle()); polymorphismDraw2(new Rectangle()); polymorphismDraw2(new Triangle());
✓ 실행 결과
원 그리기
사각형 그리기
삼각형 그리기
원 그리기
사각형 그리기
삼각형 그리기
📍 총정리
• 직원(Employee) 클래스 정의
class Employee{ String name; int salary; // 파라미터 생성자 정의 // alt + shift + s + o public Employee(String name, int salary) { super(); // Object this.name = name; this.salary = salary; } // getEmployee() 메서드 정의 public String getEmployee() { return name + ", " + salary; } // 일반 직원의 연봉 계산 public void salaryCalculation() { System.out.println("연봉 : " + salary); }
• 관리자(Manager) 클래스 정의 — Employee 상속
class Manager extends Employee{ String depart; // 부서명 int manageEmployeeCount; // 관리하는 직원 수 // 기본 생성자 // public Manager() { // super(); // }
– 기본 생성자는 이러한 형태로 생략되어 있는데, 부모클래스에 기본 생성자 정의가 되어 있지 않아 에러 발생.
→ 서브클래스가 부모클래스를 상속받기 때문에 부모클래스의 생성자가 있어야 서브 클래스도 생성 가능.
→ 바로 밑에서 파라미터 생성자로 해결하기.
// 파라미터 생성자 // alt + shift + s + o public Manager(String name, int salary, String depart, int manageEmployeeCount) { super(name, salary); this.depart = depart; this.manageEmployeeCount = manageEmployeeCount; }
– 슈퍼클래스 기본 생성자가 없으므로 슈퍼클래스의 파라미터 생성자를 호출하도록 하고,
서브클래스의 파라미터 생성자도 정의함.
– 단축키를 활용하면 슈퍼클래스의 생성자 호출과 동시에 해당 변수 초기화도 맡김. (super 패키지 설명 참조)
→ 서브클래스에서는 자기꺼만 초기화 함.
// getManager() 메서드 정의 public String getManager() { return getEmployee() + ", " + depart + ", " + manageEmployeeCount; }
– return에 중복되는 코드를 위에 같은 코드가 리턴되는 getEmployee() 메서드로 처리함.
// salaryCalculation() 메서드 오버라이딩 (관리자 연봉) // alt + shift + s + v @Override public void salaryCalculation() { int salaryResult = salary + (manageEmployeeCount * 10); System.out.println("연봉 : " + salaryResult); } }
– 매니저 연봉 = 기본연봉 + (관리직원 수 * 10만원) 이므로, 연봉계산 메서드를 매니저의 연봉에 맞게끔 오버라이딩 함.
• 엔지니어(Engineer) 클래스 정의 — Employee 상속
class Engineer extends Employee{ int numOfCertificate; // 자격증 개수 // 파라미터 생성자 정의 public Engineer(String name, int salary, int numOfCertificate) { super(name, salary); this.numOfCertificate = numOfCertificate; }
– 슈퍼클래스 기본 생성자가 없으므로,
슈퍼클래스의 파라미터 생성자를 호출하도록 하고 서브클래스의 파라미터 생성자도 정의함.
// getEngineer() 메서드 정의 public String getEngineer() { return getEmployee() + ", " + numOfCertificate + "개"; } // salaryCalculation() 메서드 오버라이딩 (엔지니어 연봉) // alt + shift + s + v @Override public void salaryCalculation() { int salaryResult = salary + (numOfCertificate * 20); System.out.println("연봉 : " + salaryResult); } }
– 엔지니어 연봉 = 기본연봉 + (자격증 수 * 20만원), 연봉계산 메서드를 엔지니어의 연봉에 맞게끔 오버라이딩 함.
• 직원 클래스 호출하기
Employee emp = new Employee("홍길동", 3000); System.out.println("Employee 정보 : " + emp.getEmployee()); emp.salaryCalculation();
– Employee 클래스의 인스턴스를 생성, 파라미터 생성자에 알맞은 데이터를 입력함.
– 직원 정보를 알기 위해 참조변수명으로 해당 인스턴스에 접근하여 getEmployee() 메서드를 호출하고,
직원의 연봉을 알기 위해 salaryCalculation() 메서드도 호출함.
✓ 실행 결과
Employee 정보 : 홍길동, 3000
연봉 : 3000
• 관리자 클래스 호출하기
Manager man = new Manager("이순신", 4000, "개발팀", 3); System.out.println("Manager 정보 : " + man.getManager()); man.salaryCalculation();
– Manager 클래스의 인스턴스를 생성하고, 해당 파라미터 생성자에 알맞은 데이터를 입력함.
– Manager의 정보를 알기 위해 참조변수명으로 해당 인스턴스에 접근하여 getManager() 메서드를 호출하고,
연봉을 알기 위해 Manager의 연봉 조건에 알맞게 오버라이딩 된 salaryCalculation() 메서드도 호출함.
✓ 실행 결과
Manager 정보 : 이순신, 4000, 개발팀, 3
연봉 : 4030
• 엔지니어 클래스 호출하기
Engineer eng = new Engineer("강감찬", 5000, 5); System.out.println("Engineer 정보 : " + eng.getEngineer()); eng.salaryCalculation();
– Engineer 클래스의 인스턴스를 생성하고, 해당 파라미터 생성자에 알맞은 데이터를 입력함.
– Engineer의 정보를 알기 위해 참조변수명으로 해당 인스턴스에 접근하여 getEngineer() 메서드를 호출하고,
연봉을 알기 위해 Engineer의 연봉 조건에 알맞게 오버라이딩 된 salaryCalculation() 메서드도 호출함.
✓ 실행 결과
Engineer 정보 : 강감찬, 5000, 5개
연봉 : 5100
• 업캐스팅
Employee emp2 = new Engineer("박보검", 10000, 5); System.out.println("Engineer 정보 : " + emp2.getEmployee()); emp2.salaryCalculation();
– Engineer타입의 인스턴스를 Employee 타입의 emp2에 저장함 → 업캐스팅
– 업캐스팅이 되면서 참조변수의 범위가 좁아졌으므로, 서브클래스의 getEngineer() 메서드를 사용하지 못함.
→ 슈퍼클래스로 부터 상속받은 생성자와 메서드만 사용 가능.
✓ 실행 결과
Engineer 정보 : 박보검, 10000
연봉 : 10100
• 다형성 활용
– 전 직원의 연봉을 슈퍼클래스인 Employee 클래스에서 모두 계산
→ Employee, Manager, Engineer 인스턴스 모두 처리해야 하므로 다형성 필요.
→ 메서드 파라미터로 다형성을 적용한(서브클래스의 인스턴스를 모두 참조하는) Employee타입의 레퍼런스가 필요.
→ 메서드 호출시 파라미터에 해당 서브클래스의 인스턴스를 입력하면 '업캐스팅' 됨.
class Employee{ // salaryCalculationAll() 메서드 정의 public void salaryCalculationAll(Employee emp) { // 업캐스팅 됨 int salaryResult = 0; // 연봉 계산 결과를 저장할 변수
– 참조영역의 축소로 인해 각 서브클래스 타입의 멤버변수는 보이지 않음.
→ Employee를 제외한 Manager, Engineer의 경우 다시 다운캐스팅을 통해 서브클래스의 멤버에 접근해야 함.
– 단, 무작정 다운캐스팅을 수행할 경우 오류가 발생할 수 있으므로
반드시 instanceof 연산자를 통해 타입 판별 후 다운캐스팅 필요.
→ 오류가 발생하면 프로그램이 중단되므로 instanceof 연산자로 안전장치를 거는 것.
if(emp instanceof Manager) { System.out.println("Employee → Manager로 다운캐스팅 가능"); Manager man = (Manager)emp; salaryResult = man.salary + (man.manageEmployeeCount * 10);
– emp 인스턴스는 Manager타입인가? 조건이 참이라면 다운캐스팅 가능.
– 자동형변환 불가하므로 강제형변환 필수.
– 업캐스팅으로 축소됐던 참조영역이 다운캐스팅으로 확대되었으므로,
다운캐스팅 된 Manager 타입 인스턴스를 통해 모든 멤버 접근 가능.
→ 기본 연봉과 관리 인원 수에 따른 인센티브를 더해서 계산 가능.
}else if(emp instanceof Engineer) { System.out.println("Employee → Engineer로 다운캐스팅 가능"); Engineer eng = (Engineer)emp; salaryResult = eng.salary + (eng.numOfCertificate * 20);
– emp 인스턴스는 Engineer타입인가? 조건이 참이라면 다운캐스팅 가능.
– 자동형변환 불가하므로 강제형변환 필수.
– 업캐스팅으로 축소됐던 참조영역이 다운캐스팅으로 확대되었으므로,
다운캐스팅 된 Engineer 타입 인스턴스를 통해 모든 멤버에 접근 가능.
– 기본 연봉과 자격증 수에 따른 인센티브를 더해서 계산.
}else if(emp instanceof Employee) { System.out.println("Employee 그대로 사용"); salaryResult = salary; }
– emp 인스턴스는 Employee타입인가? 조건이 참이라면 다운캐스팅 가능.
→ Employee 그대로 사용.
– 주의! 반드시 하위타입부터 판별을 수행해야 함.
→ 범위가 제일 큰 타입부터 조건으로 설정하면, 첫번째 if문이 무조건 참이 되므로 아래의 조건에 접근이 아예 안됨.
// 각 직원의 계산된 연봉 출력 System.out.println("연봉 : " + salaryResult + "만원 입니다."); } }
• 메서드 파라미터로 인스턴스 전달
emp.salaryCalculationAll(emp); // new Employee("홍길동", 3000); emp.salaryCalculationAll(man); // new Manager("이순신", 4000, "개발팀", 3); emp.salaryCalculationAll(eng); // new Engineer("강감찬", 5000, 5);
– 위에서 인스턴스 생성 시 파라미터 생성자에 입력한 데이터들이 인스턴스(참조변수)에 저장되었으므로
메서드 파라미터에 해당 인스턴스를 전달해줌.
✓ 실행 결과
Employee 그대로 사용
연봉 : 3000만원 입니다.
Employee → Manager로 다운캐스팅 가능
연봉 : 4030만원 입니다.
Employee → Engineer로 다운캐스팅 가능
연봉 : 5100만원 입니다.
'JAVA' 카테고리의 다른 글
상수 (Constant) (0) | 2023.04.04 |
---|---|
추상메서드와 추상클래스 (Abstract) (0) | 2023.03.29 |
static 키워드 (static 멤버 / 메모리 할당 순서 / 싱글톤 디자인 패턴) (0) | 2023.03.14 |
this 키워드 (레퍼런스 / 생성자) (0) | 2023.03.12 |
값 전달 방식에 따른 차이 (Pass by) (0) | 2023.03.12 |
- Total
- Today
- Yesterday
- 매개변수
- javascript
- jsp
- 숫자형
- gitbash
- 문자형
- 출력문
- Dao
- 데이터타입
- Git
- Java
- 제어문
- Method
- 원격저장소
- Object
- mysql
- 내장객체
- model2
- 오버라이딩
- 주석문
- 논리형
- 로컬저장소
- 인자
- 단일행함수
- 다형성
- DB
- 업캐스팅
- github
- JSTL
- null
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 |