객체지향 part에 들어오면서 내용은 점점 더 어려워지고... 진도는 쭉쭉 나가고 있어서
종종걸음으로 쫓아가느라 하루가 순식간에 지나가버리는 요즘이다!
그래도 조금씩 코드에 대한 해석도 가능해지고, 작성도 어느정도 할 수 있다는게
신기하고, 뿌듯하고, 보람차고, 더 열심히 해봐야겠다는 의지도 더욱 생겼다 :)
메소드 Override(오버라이딩) 예제 01
지난 시간에 이어 메소드 Override를 직접 구현하여 활용하는 예제 문제를 풀어보았다.
먼저 동일한 패키지에 존재하는 Employee, RegularEmployee, TempEmployee 클래스와
다른 패키지에 존재하는 EmployeeMain 클래스를 생성해준다.
그리고 제시된 각 클래스의 필드와 메소드를 이용해 다음과 같은 출력이 가능하도록
각 클래스에 필드, 메소드 등을 정의하는 코드를 작성해보는 것으로 오늘 하루도 시작!
다행히 차근차근 코드를 하나씩 작성하다보니 정상적인 코드를 완성할 수 있었다.
먼저 상위 클래스인 Employee에서 공통적인 필드를 정의하고, 기본생성자에서 초기화 한 다음
하위 클래스에서 오버라이딩하여 메인메소드에서 호출해야하는 getPayMonth() 메소드를 정의해준다.
이 때 Employee 클래스에서 getPayMonth() 메소드의 반환값은 임의로 설정해주어도 무방하다.
그리고 정규직과 계약직의 월급여 계산 방법이 각각 다르므로 계산식에 활용되는 필드를
하위 클래스 TempEmployee와 RegularEmloyee에서 각각 정의하고, 초기화해준다.
이 때 하위클래스에서 필드를 초기화 해줄때 코드의 첫 줄에서 super()를 이용해
기존 상위 클래스에서 초기화 된 필드를 사용할 수 있다.
마지막으로 하위 클래스에서 각각 getPayMonth() 메소드를 오버라이딩 하여
월급여 계산 방법을 적용한 계산식을 반환값으로 설정해 준 다음,
메인메소드에서 각각 하위 메소드 객체 생성과 필드값 입력을 통하여
getPayMonth() 메소드를 호출해주면 월급여를 계산하여 출력해준다.
메소드 Override(오버라이딩) 예제 02
앞서 풀어본 예제와 같은 방식의 예제를 또 한 번 풀어보았다.
이번에는 모든 클래스가 같은 패키지에 존재하도록 생성해주고,
unitList 필드는 List<String> 타입으로 정의해주어 활용하는 예제이다.
앞선 예제와 조금 다른 점은 unitList 필드에 할당되는 값을 입력값으로 설정하기 위하여
상위 클래스에서 unitList 필드의 Setter를 생성해주고, 메인메소드에서 이를 확인하기 위한 Getter를 생성해준다.
그리고 각 하위 클래스인 Factory와 Barracks에서는 setUnitList() 메소드를 오버라이딩하여
List<Stirng> 타입에 할당하기 위해 필드명.add("값") 형태로 문자열 값을 입력해준다.
그리고 메인메소드에서 각 하위 클래스의 객체를 생성할 때에는
상위 클래스의 매개변수가 2개인 생성자에 접근하여 해당되는 필드값을 입력해주고,
오버라이딩 하였던 메소드를 호출해 준 다음 출력해보면 저장된 값을 나열하는 UnitList를 확인할 수 있다.
static 멤버의 Override?
생성자와 메소드는 Override가 가능한 클래스 멤버이지만
static으로 정의된 정적 멤버일 경우에는 상속의 대상이 아니므로 Override가 불가능하다.
그러나 위의 코드처럼 2개의 클래스에서 같은 이름으로 생성된 static 메소드를
메인메소드에서 각각 호출하여 정상적으로 실행할 수 있는데, 이를 하이딩이라고 부른다.
기존의 Override에서는 하위 클래스의 메소드가 상위 클래스의 메소드 기능을 모두 포함했다면
위와 같이 static 메소드에서의 하이딩은 각각의 메소드 기능만을 갖는다.
접근제한자 protected
|
다음은 지난 수업 시간에도 다루었던 내용 중 접근제한자 protected에 대한 복습 시간!
패키지를 여러개로 나누어 각각 클래스를 생성하고, 접근제한자 protected의 접근 가능 범위를 살펴보았다.
먼저 개념으로 정의된 접근제한자 protected는 동일패키지에 존재하는 클래스
또는 상속관계에 있는 외부 패키지의 클래스에서 접근이 가능하고, public에 비해서는 캡슐화 된 접근제한자이다.
실제 코드에서 확인해보기 위해 상위 클래스 FactoryUnit에서 protected 필드를 선언해주고,
repare() 메소드를 Override 대상으로 하여 하위 클래스 Goliath을 생성하였다.
Goliath 클래스는 FactoryUnit 클래스와 다른 패키지에 존재하는 클래스이지만
FactoryUnit 클래스를 상속하고 있는 하위 클래스이므로 상위 클래스의 필드를 호출할 수 있다.
또한 새로운 필드와 메소드를 정의할 수 있으며, Override 대상으로 지정하였던 repare() 메소드도 새롭게 정의할 수 있다.
추가로 입력된 필드값을 저장하기 위해 Setter를 생성해주고, 입력값에 대한 최댓값과 최솟값의 범의를 상수로 정의하여
조건문을 활용해 조건에 따른 지정값을 정의하여 반환하는 Setter 메소드를 생성할 수 있다.
메인메소드에서 호출이 가능한지 확인하기 위해 동일 패키지에 FactoryUnitExample 클래스를,
다른 패키지에 GoliathDemo 클래스를 생성하여 정의된 필드를 모두 호출해보았다.
먼저 상위 클래스와 동일한 패키지에 존재하는 FactoryUnitExample 클래스에서는
상위 클래스를 참조하는 객체를 생성하여 모든 필드를 직접 호출하는 것이 가능한 것을 확인할 수 있었다.
그리고 다른 패키지에 존재하는 Goliath 클래스는 상위 클래스를 상속하는 클래스가 아니므로
protected 접근제한자를 갖는 필드에 직접적인 접근이 불가능하기 때문에
public 타입의 Goliath 클래스를 참조하는 객체를 생성하여 Setter를 호출하고, 동시에 필드값을 입력하여 저장하는 것으로
상속을 하지않더라도 하위클래스의 세터를 활용함으로써 동일한 출력 결과를 확인할 수 있었다.
클래스의 타입변환
데이터 타입의 변환에서 더 큰 크기의 타입에 작은 크기의 타입을 할당하면 자동타입변환이 실행되고,
더 작은 크기의 타입에 큰 크기의 타입을 할당하려면 casting을 통해 강제타입변환을 해주어야한다.
(Line5 - Line 14의 내용은 데이터의 타입변환 복습을 위해 작성한 코드이다)
이처럼 클래스도 타입의 변환이 발생하는데, 상위 클래스인 슈퍼 클래스를 크기가 더 큰 타입으로 간주하고,
하위 클래스인 서브 클래스를 크기가 더 작은 타입으로 간주하여
데이터의 타입 변환과 동일하게 자동타입변환, 강제타입변환 적용이 가능하다.
위의 코드에서 예를 살펴보면 상위 클래스인 Animal, 하위 클래스인 Dog, Cat을 생성한 후
메인메소드에서 Cat, Dog 객체를 Animal 객체에 할당하면 자동타입변환이 이루어지고,
Animal 객체를 Cat, Dog 객체에 할당하려면 강제타입변환을 해주어야 하는 부분을 확인할 수 있었다.
클래스의 활용
다음은 클래스를 참조타입으로 활용하는 것을 확인해보는 예제이다.
여기서 주목해야할 점은 기존에 배웠던 기본 타입, 참조 타입 외에도
생성 및 정의된 클래스는 참조 타입으로 활용할 수 있다는 것이다.
간단하게 이해하기 위해서는 참조 타입 String 또한 java에 정의된 String 클래스임을 확인해보면 된다.
Hero 클래스에 Item 참조 타입을 활용하였을 때, Hero가 Item을 가진다고 하여 hasA 관계라고 하고,
상속된 하위 클래스에서 상위 클래스의 기능을 그대로 가지는 것을 비유하여 isA 관계라고 한다.
대부분 하위 클래스는 상위 클래스의 기능을 그대로 가진 상태에서 필드, 생성자, 메소드를 추가로 갖기 때문에
하위 클래스가 상위 클래스를 포괄하는 개념의 관계로 생각하면 된다.
추가적으로 Item 클래스가 Hero 클래스에 상속된 것은 아니라는 점을 유념해야한다.
Item 클래스가 Hero 클래스 내에서 참조 타입으로 사용되면서 Hero 클래스에 속해지는 개념으로 생각하면 좋을 것 같다.
그렇기 때문에 메인메소드에서 Item 객체와 Hero 객체를 각각 생성하여 필드 값을 할당 해주어야하고,
그런다음에야 Hero 클래스의 printInfo() 메소드를 호출하여 저장된 정보를 확인해 볼 수 있다.
클래스의 다형성 01
이번에는 클래스의 다형성을 살펴보기 위한 예제이다.
상위 클래스 Item와 하위 클래스 Sword, Spear을 생성해준다.
상위 클래스에서 필드를 선언 및 초기화해주고, 하위 클래스에서는 상위 클래스의 생성자를 불러온다.
그리고 상위 클래스에서는 아이템의 이름을 출력하기위한 Getter를 생성해준다.
그리고 Hero 클래스를 생성하여 필드를 정의해주는데,
이 때 String 타입의 필드 name, 그리고 Item 타입의 필드 item을 선언해준다.
Sting 타입의 필드 name은 생성자에서 초기화해주고,
Item 타입의 필드 Item은 Setter를 생성하여 Item 클래스의 name 필드 Getter를 활용해 초기화해준다.
그리고 메인메소드에서 호출하여 확인하기 위한 info() 메소드도 정의해준다.
마지막으로 메인메소드에서 Hero 객체를 생성, name 필드 값을 할당해주고,
Sword, Spear 객체를 각각 생성, name 필드 값을 할당해 준다.
그리고나서 Hero 클래스의 Setter를 호출 한 다음 info() 메소드를 호출해주면
각각 할당된 Item 클래스의 name 필드값을 갖는 info() 실행문을 출력해준다.
클래스의 다형성 02
클래스의 다형성을 다른 형태의 예제로 확인해보았다.
상위 클래스인 Hero와 하위 클래스인 Arthas에서 useUlt() 메소드를 오버라이딩 해주고,
Arthas 클래스에서 새로운 메소드 infoArthas를 정의해 준 다음 메인메소드에서 객체를 생성하여 호출한다.
새로운 객체를 각각 Arthas의 참조변수, Hero의 참조변수에 할당해주고,
메소드를 각각 호출해주면 오버라이딩 메소드는 모두 Arthas의 useUlt()메소드에 접근하고,
Hero의 참조변수는 Arthas에서 새롭게 정의된 infoArthas 메소드에 접근이 불가능하다.
즉, Hero의 참조변수 a2는 Hero 클래스에 정의된 메소드에만 접근이 가능한 것이다.
상위 클래스인 Hero에서 useUlt 메소드는 하위 클래스인 Arthas에서 오버라이딩하여
결과적으로 몸체를 갖지않는 메소드이기 때문에 추상을 뜻하는 abstract를 붙여주어 추상화하고,
추상메소드를 갖는 클래스는 반드시 추상클래스가 되어야하기 때문에 클래스에도 abstract를 붙여준다!
추상클래스 내에는 필드, 일반 메소드, 생성자, 추상메소드를 정의할 수 있고,
추상클래스에서는 new 연산자로 객체를 생성하는 것이 불가능하다.
추상클래스 / 추상메소드
interface / implements
마지막으로 메소드의 다형성을 예제를 통해 다시 한 번 확인하면서
추상클래스와 추상메소드, interface와 implements의 활용에 대해서도 미리 배웠다!
앞서 추상클래스와 추상메소드의 사용법을 살펴보았는데,
추상클래스를 interface 객체로 정의하면 추상메소드에서 abstract를 생략할 수 있고,
Class를 상속할 때는 extends를 썼다면 interface를 상속할때는 implements를 사용해주면 된다.
그러면 메인메소드에서 sword와 spear을 각각 새로운 객체로 생성하여
오버라이딩 되었던 option() 메소드를 호출하였을 때 각 클래스에 정의된 메소드를 실행하게 된다.
진도를 더 나가면 구체적으로 배울 내용이기 때문에 잘 기억해두기로 :)
'Develop growth diary > JAVA' 카테고리의 다른 글
자바 기반 객체지향 프로그래밍07 - 클래스의 다형성 (1) | 2022.10.18 |
---|---|
자바 기반 객체지향 프로그래밍 06 - 클래스의 다형성(List<>, interface) (0) | 2022.10.13 |
자바 기반 객체지향 프로그래밍 04 - 상속(Override - 접근제한자, super, toString) (0) | 2022.10.11 |
자바 기반 객체지향 프로그래밍 03 - 정적 멤버, 접근제한자 (0) | 2022.10.07 |
자바 기반 객체지향 프로그래밍 02 - 클래스 멤버와 메소드&생성자 오버로딩 (0) | 2022.10.06 |