OOP Lab 6

Due date : April 26,   23 : 59

 

·       Submit your assignment using the following file format:

 LabNumber_StudentName_Student_ID.zip

 Example: Lab6_Hongkildong_201620505.zip

·       The  zip file will contain two types of files, namely:

1)      report file with file format  Report_Lab number (eg. report_6) to answer theory questions and to write the screen shot that display the output of your program 

2)     Source code file that contains classes to answer programming questions.


I. Objectives

1.  Learning the similarity and difference between inheritance and polymorphism, and how to utilize them.

2. Learning the similarity and difference between method overriding (dynamic binding) and method overloading (static binding) in terms of polymorphism.

3.  Learning the similarity and difference between of concrete class and abstract class and how to utilize them

4.  Learning the similarity and difference between abstract class and interface and how to use them.


II. Questions(30 points)


1.   Answer the following questions using CommissionEmployee  class (Fig 9.10), BasePlusCommissionEmployee class (Fig 9.11) and PolymorphismTest  class(Fig 10.1).

a.      Find all places where polymorphism is used in Fig 10.1. (2 points).

b.      In Fig 10.1, when you insert the code in the following box between line 34 and line 35 and run the program,  why an error is generated. Explain your answer? (2 points)

 

Answer : "commissionEmployee2" is superclass( "CommisssionEmployee" type)  and "bpce" is subclass ("BasePlusCommisssionEmployee" type).

The subclass "bpce" does not contain the superclass "commissionEmployee2".

 

commissionEmployee2 는 상위클래스의 CommissionEmployee 타입이고,

bpce는 하위클래스의 BasePlusCommissionEmployee 타입이다.

하위 클래스인 "bpce"는 상위 클래스인 "commissionEmployee2"를 담지 못한다.

BasePlusCommissionEmploye bpce = commissionEmployee2; 

System.out.println("Salary = " + bpce.getBaseSalary() );

 

c.      Re-write the code in (b) correctly by yourself, run the program and capture screen shot(4 points) 

BasePlusCommissionEmployee bpce =  (BasePlusCommissionEmployee) commissionEmployee2; 

System.out.println("Salary = " + bpce.getBaseSalary());

 

d.     When you replace the code in (b) by the code in the following box, error is generated when you run the program. Why the error is generated? Explain your reason? (3 points)

 

Answer : This is because the super class "commissionEmployee" cannot be transformed into a lower class, "BasePlusCommissionEmployee."
In other words, "CommissionEmployee" ,a reference variable for "commissionEmployee", is a super class than the "BasePlusCommissionEmployee" reference variable.

 

 

"commissionEmployee"의 참조 변수는 "CommissionEmployee"인데, 하위 클래스의 "BasePlusCommissionEmployee"로 형변환 할 수 없다.

다시 말해서 하위 클래스인 "BasePlusCommissionEmployee"의 참조변수에 상위 클래스인 "CommissionEmployee" 타입의 참조 값을 담

지 못한다.

 

상위 클래스인 "commissionEmployee"는 하위 클래스인 "BasePlusCommissionEmployee"로 형변환 할 수 없기 때문이다.

다시 말해 "commissionEmployee"의 참조변수인 "CommissionEmployee"가 "BasePlusCommissionEmployee" 참조변수보다 상위 클래스이기 때문이다.

BasePlusCommissionEmployee bpce = (BasePlusCommissionEmployee) commissionEmployee; 

System.out.println("Salary = " + bpce.getBaseSalary());

2.   Answer the questions based on codes from Fig 10.4 ~ 10.9 :

·       Fig. 10.4 Super Abstract class class( Employee) 

·       Fig. 10.5 - Fig 10.7 : Subclasses(SalariedEmployee,CommissionEmployee,HourlyEmployee)

·       Fig 10.8 : sub-subclass  (BasePlusCommissionEmploye)

·       Fig.10.9 Test class ( PayrollSystemTest).

 

a.      As indicated in Fig.10.9, the program has two different outs ( see 5/6 and 6/6).  The line of codes from line 9 to line 30 are related to the output in figure 10.9 ( 5/6). The line of codes from line 33 to line 65 are related to the output in figure 10.9(6/6).  The two outputs are the same except the earned amount although their implementation is different. What type of implementation technique the codes from line 9 to line 30 follow and what type of implementation technique the codes from line 33 to line 65 follow? (2 points)

그림 10.9에 표시된 것처럼, 프로그램은 두 개의 다른 아웃(5/6과 6/6 참조)을 가지고 있다. 9호선부터 30호선까지의 코드 라인은 그림 10.9(5/6)의 출력과 관련이 있다. 33호선부터 65호선까지의 코드 라인은 그림 10.9(6/6)의 출력물과 관련이 있다. 이 두 산출물은 실행은 다르지만 "벌어들인" 금액을 제외하고 동일하다. 9호선부터 30호선까지의 코드는 어떤 유형의 구현 기법을 따르고 9호선부터 30호선까지의 코드는 어떤 유형의 구현 기법을 따르고 있는가?

 

Answer : 

9 ~ 30 line : The program is implemented without polymorphism.

33 ~ 65 line : The program is implemented through polymorphism, confirming that "casting" is possible through "instanceof".

 

9 ~ 30 line : polymorphism 을  쓰지 않은 채로 구현하고 있다.

33 ~ 65 line : instanceof 를 통해 형변환이 가능한지 확인하면서 polymorphism을 통해 구현하고 있다.

 

b.     Change the code in Employee class in Fig.10.4  as follows,

·       deleting abstract keyword at  line 4 and becomes:  public class Employee  

·       Change line 46 by the code: public double earnings() { return 0.0; }

  After modification, is the program generate different result output? Say yes or no (2pt)

 

Answer : Yes

c.     Before modification and after modification in (b), add the following code to the main function and run the program.  What is the difference between the two?  Explain  your reason (3 points)

 

Answer : 

Delegate implementation to users by inserting the "abstract" keyword.
To force to use by inheriting.

 

 "abstract" keyword를 넣음으로써 사용자에게 구현을 위임한다. 상속해서 사용하도록 강제한다.

 

Employee e = new Employee("Kildong", "Lee", "000-00-0000");

3.    Answer the questions based on codes from Fig 10.11 ~ 10.15 :

·       Payable  super interface (Fig. 10.11)

·       Invoice concrete sub-class(Fig.10.12),

·       Employee Abstract sub-class (Fig 10.13),

·       SalariedEmployee   concrete sub-sub class (Fig.10.14)

·       PayableInterfaceTest( Fig 10.15).

 

a.    Modify the program by adding the following classes.   See Fig.10.2 and Fig.10.10.

·       HourlyEmployee : Child class of Employee : sibling of SalariedEmployee 

·       CommissionEmployee: child class of Employee   or sibling of SalariedEmployee

·       BasePlusCommissionEmployee: child-class of CommissionEmployee:

·       Hint:  referee Fig.10.2 and Fig.10.10 to understand the problem.

 

  Write the code of the above three classes by replacing their earnings () methods using getPaymentAmount()  method similar to the SalariedEmployee class( hint: refer  Fig.10.6,  Fig.10.7, and Fig.10.8). (3 points)

위의 세 클래스를 earning() 를 getPaymentAmount()로 대체 함으로써 바꿔라. SalariedEmployee class와 비슷하게.

(hint : hourly , CommissionEmployee, Baseplus)

b.     Modify also PayableInterfaceTest(Fig.10.15) class by expanding the array, so that the class can create at least two objects of each subclass and calculate the payment (3 points) 

 


4.      Dog, Cat, and Sheep are all animals with their own unique crying style. Answer the following questions.

a.     Define a class for each of the three types of animals including at least one method as shown in the following box. The body of the cry() method   prints  a string corresponding to the  sound of the crying style (3 points).

다음 상자에 표시된 것처럼 적어도 하나의 방법을 포함하여 세 가지 동물 유형 각각에 대한 클래스를 정의하십시오. 크라이() 방법의 본체는 크라이 스타일(3점)의 소리에 해당하는 스트링을 인쇄한다.

 Example:  

·       sound of dog: waw waw waw

·       sound of Cat:  Miyaw Miyaw Miyaw

·       sound of sheep: Ba Ba Ba 

void cry()

{

...

}

 

b.      Run AnimalTest class that tests the crying of these animals by creating at least two objects of each sub animal. You code should include both non-polymorphism and polymorphism approaches (3 points). Hint refer Fig 10.9

각 하위 동물의 두 개 이상의 개체를 만들어 이러한 동물의 울음소리를 테스트하는 "AnimalTest " 클래스를 실행하십시오. 코드는 비폴리모피즘과 다형성 접근법(3점)을 모두 포함해야 한다. 힌트 참조 그림 10.9


@Override 라고 annotation을 다는 이유

일종의 안전장치입니다. 예컨대 어떤 라이브러리에 속한 클래스를 상속받아 특정 메서드를 오버라이드했는데, 그 라이브러리가 업데이트 되면서 해당 메서드의 시그네쳐가 바뀌는 경우를 생각할 수 있습니다.

이 경우 이전엔 오버라이드한 메서드가 새버전에서는 그냥 새로 추가된 메서드로 인식될 뿐 컴파일 오류가 발생하지 않기 때문에 개발자는 동작이 달라진 걸 알아채기 어렵습니다.

이런 부류의 문제를 방지하기 위해 `@Override`를 사용하는 것이고, 만일 해당 어노테이션이 붙은 메서드가 부모 메서드를 오버라이드하지 않는다면 컴파일시 오류가 발생하게 됩니다.


 instanceof 연산자 

instanceof는 객체 타입을 확인하는데에 쓰인다.

속성은 연산자이고 형변환이 가능한지 해당 여부를 true 또는 false로 가르쳐준다.

부모 객체인지 자식 객체인지 확인하는데 쓴다고 생각하면 된다.

 

(자손) instanceof (부모, 자기자신)

void doWork(Car c) 에서 

매개변수에 Car 또는 FireEngine, Ambulance 같은 모든 자손이 들어올 수 있다.

뭐가 들어올 지 모르기 때문에 형변환이 가능한지 확인해 줘야한다. 이때 쓰는 것이 instanceof 이다.

들어온 c가 가리키는 객체가 FireEngine이냐? 혹은 FireEngine의 자손이냐? 

맞으면 c를 형변환 시켜준다.

 

형변환을 하는 이유는 인스턴스의 원래 기능을 모두 사용하려고.

Car 타입의 리모콘인 c로는 water()를 호출할 수 없으니까.

리모콘을 FireEngine으로 바꿔서 water()를 호출


getClass() 메서드

java.lang의 Object 클래스 중에서 getClass() 메소드에 대해 알아보자.

현재 참조하고 있는 클래스를 확인할 수 있는 메소드이다.

A라는 클래스를 참조하고 있다면 class A 라는 값이 출력된다.

 

객체 타입을 확인하는데 쓰이는 instanceof 와 함께 알아두면 유용하다.

classes 라는 패키지 안에 A,B 클래스를 참조하고 있음을 알 수 있다.


 new 연산자 

객체와 인스턴스 및 메모리(heap 영역)에 대한 포스팅에서 new 연산자라는 것을 사용하였는데 new 연산자가 어떻게 사용되고 어떤 역할을 하는지에 대해서 알아보도록 하자.

new 연산자

클래스    객체변수    =   new    클래스();

 앞 포스팅들에서 알아본 내용이다. 한 줄밖에 안되는 코드이지만 여기에는 많은 내용들이 함축되어 있다. 복습 차원에서 간단히 설명하면 인스턴스(객체)를 생성할 때 사용하는 코드이며 객체 변수가 실제 데이터가 아닌 참조 값을 갖는다는 내용이 포함되어 있다. 그러면 저기에서 사용되는 new라는 것은 무엇인지 알아보자.

 new는 클래스 타입의 인스턴스(객체)를 생성해주는 역할을 담당한다. 여기에 조금 더 설명을 붙이면 new 연산자를 통해 메모리(Heap 영역)에 데이터를 저장할 공간을 할당받고 그 공간의 참조값(reference value /해시코드)을 객체에게 반환하여 주고(메모리, 객체 및 인스턴스 포스팅 참고) 이어서 생성자를 호출하게 된다. 인스턴스를 핸들하기 위해서는 new 연산자를 통해 참조값을 저장한 객체로만 접근이 가능하다. 정리하면 아래와 같다.


 super 클래스 

super 키워드는 부모 클래스를 의미한다. 여기에 ()붙이면 부모 클래스의 생성자를 의미하게 된다.

이렇게 하면 부모 클래스의 기본 생성자가 없어져도 오류가 발생하지 않는다.

하위 클래스의 생성자에서 super를 사용할 때 주의할 점은 super가 가장 먼저 나타나야 한다는 점이다.

즉 부모가 초기화되기 전에 자식이 초기화되는 일을 방지하기 위한 정책이라고 생각하자.

 

다시 말하지만 초기화 코드를 super 보다 먼저 등장시키면 안된다.

항상 하위클래스의 초기화 코드는 super클래스를 호출한 다음에 나타나야 한다. 안 그러면 오류가 나타난다.

하위클래스가 만들어진다는 것은 상위클래스가 이미 인스턴스화 되었다는 것이니까,

상위클래스가 미리 초기화가 다 끝난 상태에서만 하위 클래스의 초기화를 진행할 수 있다는 것이다.


 toString() 메소드란 

"Object"클래스가 가진 메소드 중 "toString"메소드가 있습니다.

물론 "Object" 클래스의 모든 메소드는 모든 클래스가 사용이 가능합니다.

"toString" 메서드는 객체가 가지고 있는 정보나 값들을 문자열로 만들어 리턴하는 메소드 입니다.

 

 

이렇게 출력해보면 결과값에는 이상한 정보가 담기는데

이 값은 순수 "Object"의 "toString"결과 값입니다. (결국 의미없는 디폴트 값)

 

하지만 "String" 클래스나 "File"클래스에서는 "toString"에 메소드를 재정의 하여 의미있는 값을 리턴해 줍니다.

 

 

"String" 클래스 객체의 "toString"메소드는 자신이 가진 값을 그대로 리턴해주고,

"File"클래스 객체의 "toString"메소드는 자신이 가진 해당 경로값을 리턴해 주고있습니다.

 

이 두 클래스는 내부에서 "toString"을 재정의해서 사용한 것 입니다.

 

증거로는 이클립스 자동완성 창에서 보여줄 수 있습니다.

 

- "String"클래스의 "toString"메소드 상세 설명 -

 

빨간 네모 부분 보면 "Overrides : to String in class Object"라고 써 있는데

오버라이드 즉, 재정의했다는 의미 입니다.


toString() 메소드를 재정의해서 사용

( 재정의 : 정의되어 있는 변수나 배열 요소가 다시 정의되는 것 )

 

아까전 의미없던 값 "Human"클래스로 재정의 해보겠습니다.

 

콘텍스트 메뉴로 오버라이드할 메소드를 찾을수 있습니다.

 

ok를 누르면

 

오러라이딩 함수가 생성되는데

이 함수를 재정의 하여 사용하도록 하겠습니다

 

빨간 네모처럼 나이를 리턴하도록 만들고

실제로 사용해 보도록 하겠습니다.

 

 

"Human"클래스의 객체를 생성하여 "toString" 메소드를 사용해 보았습니다.

우리가 재정의 했던 "toString"메소드가 잘 호출 되었음을 출력값을 통해 알 수가 있습니다.


toString() 메소드는 자동으로 호출된다

toString 매소드는 자동으로 호출 됩니다.

 

우리가  자주 사용하던 "String" 클래스 객체를 선언하여 

"toString"메소드 없이 그냥 객체 자체로 변수에 담긴 내용을 가져와 사용했었습니다.

 

 

위 코드를 보시면 "str"이라는 "String"클래스의 객체 입니다.

 

그런데 신기하게도 객체임에도 불구하고 

"str"독단적으로 저렇게 사용 되어지고 있습니다.

 

바로 이 지점에서 "toString"이 자동으로 호출 되는 것입니다.

이것을 증명하기 위해서 우리가 구현한 "Human"클래스로 한번 시도 해보겠습니다.

 

 

아까 만들었던 소스에서 "toString"만 제거 하였습니다.

출력 결과는 잘나오고 있는걸 확인할수 있습니다.

 


 다형성 (Polymorphisim) 

다형성은 자바에서 매우 중요한 개념이다. 하지만 다형성 자체가 어렵다거나 새로운 기법에 대한 내용은 아니다. 이전에 알아본 (추상) 클래스 상속, 인터페이스 구현, 레퍼런스 형변환(업캐스팅), 오버라이딩 등 많은 내용들을 이용하여 프로그램을 효율적으로 만들 수 있는 방법이다. 따라서 앞에서 알아본 내용들에 대한 개념의 충분한 이해가 있다면 쉽게 이해가 가능하다. 이전 내용들을 잘 모르겠다면 위의 링크된 페이지에서 확인 후 밑의 내용을 보는 것이 좋다.

 

다형성(Polymorphism)

 다형성이란 같은 자료형에 여러 가지 객체를 대입하여 다양한 결과를 얻어내는 성질을 의미한다. 

다형성의 효과

 하나의 타입으로 다양한 실행 결과를 얻을 수 있으며 객체를 부품화하여 유지 보수를 용이하게 한다. 

다형성 구현 방법

 클래스의 상속이나 인터페이스를 구현하는 자식 클래스에서 메서드를 재정의(오버라이딩) 하고 자식 클래스를 부모 타입으로 업캐스팅한다. 그리고 부모 타입의 객체에서 자식 멤버를 참조하여 다형성을 구현한다. 이전 포스팅에서 알아보았던 업 캐스팅의 예제도 다형성의 방법이다.

비유와 예제

 위의 내용만으로는 이해하기에 어려울 수 있다. 간단한 비유와 그것에 대한 예제를 통해서 알아보도록 하자.
 요새 많이 하는 게임 중에 오버워치라는 것이 있다. 이 게임에는 다양한 캐릭터가 있고 캐릭터마다 서로 다른 스킬들을 갖고 있다. 좌 클릭을 하면 각 캐릭터 마다에 기본 공격을 하고 우 클릭을 하거나 Shift, E, Q 버튼을 클릭하면 각 캐릭터마다 갖고 있는 고유한 스킬을 사용하게 된다. 즉, 오버워치라는 하나의 인터페이스에서 그것을 구현하는 각 캐릭터(객체) 들에 대해 동일한 버튼(메소드 오버라이딩)을 클릭하여 서로 다른 스킬을 사용하게 된다(다형성). 그러면 이것을 소스 코드를 통해서 알아보도록 하자.

import java.util.Scanner;

interface OverWatch { // 인터페이스
	void name(); // 추상 메소드
	void lClick(); // 추상 메소드
	void rClick(); // 추상 메소드
	void shiftButton(); // 추상 메소드
	void eButton(); // 추상 메소드
	void qButton(); // 추상 메소드
}

class Mei implements OverWatch { // 인터페이스 구현 클래스
	public void name() { // 오버라이딩
		System.out.println("이름 : 메이");
	}
	public void lClick() { // 오버라이딩
		System.out.println("좌클릭 : 냉각총");
	}
	public void rClick() { // 오버라이딩
		System.out.println("우클릭 : 고드름 투사체");
	}
	public void shiftButton() { // 오버라이딩
		System.out.println("shift : 급속 빙결");
	}
	public void eButton() { // 오버라이딩
		System.out.println("e : 빙벽");
	}
	public void qButton() { // 오버라이딩
		System.out.println("q : 눈보라(궁극기)");
	}
}

class Reaper implements OverWatch { // 인터페이스 구현 클래스
	public void name() { // 오버라이딩
		System.out.println("이름 : 리퍼");
	}
	public void lClick() { // 오버라이딩
		System.out.println("좌클릭 : 헬파이어 샷건");
	}
	public void rClick() { // 오버라이딩
		System.out.println("우클릭 : 없음");
	}
	public void shiftButton() { // 오버라이딩
		System.out.println("shift : 망령화");
	}
	public void eButton() { // 오버라이딩
		System.out.println("e : 그림자 밟기");
	}
	public void qButton() { // 오버라이딩
		System.out.println("q : 죽음의 꽃(궁극기)");
	}
}

class Mccree implements OverWatch { // 인터페이스 구현 클래스
	public void name() { // 오버라이딩
		System.out.println("이름 : 맥크리");
	}
	public void lClick() { // 오버라이딩
		System.out.println("좌클릭 : 피스키퍼");
	}
	public void rClick() { // 오버라이딩
		System.out.println("우클릭 : 모든 총알 발사");
	}
	public void shiftButton() { // 오버라이딩
		System.out.println("shift : 구르기");
	}
	public void eButton() { // 오버라이딩
		System.out.println("e : 섬광탄");
	}
	public void qButton() { // 오버라이딩
		System.out.println("q : 황야의 무법자(궁극기)");
	}
}

public class PolymorphismEx01 {
	public static void main(String[] args) { // main 메소드
		OverWatch ow; // 인터페이스 객체 선언
		System.out.println("플레이할 캐릭터 번호 선택(1. 메이, 2. 리퍼, 3. 맥크리)");
		Scanner sc = new Scanner(System.in); // 스캐너 객체
		int n = sc.nextInt();
		if(n==1){
			ow = new Mei(); // 업캐스팅
		}else if(n==2){
			ow = new Reaper(); // 업캐스팅
		}else{
			ow = new Mccree(); // 업캐스팅
		}
// 선택한 조건에 따라서 부모 객체로 자식 메소드 사용(하나의 타입으로 다양한 결과를 얻어냄 / 다형성)
		ow.name();
		ow.lClick();
		ow.rClick();
		ow.shiftButton();
		ow.eButton();
		ow.qButton();
	}
}

코드는 길지만 어려운 내용은 아니다. 우선 OverWatch라는 인터페이스를 정의하고 추상 메소드를 선언하였다. 이것을 구현하는 클래스 3개(Mei, Reaper, Mccree)를 정의하고 메소드를 재정의(오버라이딩) 하였다.

main 메소드에서 인터페이스(OverWatch) 객체를 선언하고 플레이할 캐릭터의 번호를 입력한다. if문에서 플레이할 캐릭터에 따라서 자식 클래스를 부모 객체로 업캐스팅 하고 부모 객체를 통해서 자식 메소드를 호출하는 내용이다. 즉, 플레이할 캐릭터에 따라서 인터페이스를 통해 아래와 같이 다양한 결과를 얻어낼 수 있고 이것이 다형성을 의미한다.

 

 

 

'📌 java > Object-oriented Programming' 카테고리의 다른 글

java - 업캐스팅 다운캐스팅  (0) 2020.04.26
java - 상속의 참조 관계  (0) 2020.04.26
Homework_W5  (0) 2020.04.14
Homework_W4  (0) 2020.04.07
Homework_W3  (0) 2020.03.31
복사했습니다!