추상 클래스와 객체의 형변환
■ 추상 클래스와 내부 클래스
추상메서드
- 메서드의 시그니쳐( 리턴타입, 메서드명, 매개변수 )만 정의
- 구체적인 행위는 정의되지 않은 특수한 메서드
- abstract 예약어를 사용
- 상속을 통해 사용되는 클래스마다 필수로 가져야하는 메서드의 형태를 정의
- 추상 메서드를 Overriding하지 않으면 컴파일 불가능
추상 클래스
- 추상 클래스는 일반적으로 하나 이상의 추상 메서드를 포함
- 추상 메서드를 포함하고 있으면 무조건 추상 클래스로 선언해야함.
- 추상 클래스는 객체 생성을 할 수 없음
- 추상 클래스를 상속받은 클래스는 추상 메서드를 OVerriding해야 객체를 생성할 수 있음
- 유지보수의 편의성을 높이기 위해 사용함. (상속을 통해 동일한 기능을 포함한 클래스가 됨)
내부 클래스
- 클래스가 다른 클래스를 포함하는 경우 내부에 포함된 클래스를 내부 클래스라고 함
- 정의되는 위치에 따라 멤버 클래스와 지역 클래스로 나뉨
멤버클래스
- 멤버 변수와 동일한 위치에 선언된 내부 클래스
- static예약어가 붙은 static멤버와 instance멤버로 나뉨
- 동일한 클래스 뿐만 아니라 다른 클래스에서도 활용 가능
- 클래스의 멤버 변수와 성격이 비슷
##### instance 멤버 내부 클래스 * 클래스의 멤버와 동일한 위치에서 선언되는 내부 클래스 * 다른 외부의 클래스에서도 사용 가능
class Outside{ // 외부 클래스 (Top Level 클래스라고도 함)
public class Inside{ // instance 멤버 내부 클래스
}
}
public class InnerClassTest{
public static void main(String[] args){
Outside outer = new Outside(); // 내부 클래스의 객체 생성을 위해 외부 클래스의 객체를
Outside.Inside inner = outer.newInside(); // 생성해야 함.
}
}
static 멤버 내부 클래스
- 외부 클래스 객체를 생성하지 않고도 내부 클래스 객체를 생성할 수 있음
class Outside{
public static class StaticInner{ // static 예약어를 이용하여 정의
}
}
public class InnerClassTest{
public static void main(String[] args){
Outside.StaticInner sin = new Outside.StaticInner();
} // static 내부 클래스 객체를 생성할 때 외부 클래스 객체를 생성하지 않아도 됨.
}
지역 클래스
- 메서드 내에 클래스가 정의되어 있는 경우
- 지역 클래스( 이름 있음 )와 무명 클래스( 이름 없음 )로 나뉨
- 활용 범위가 메서드 블록 내부로 제한
- 지역 변수와 성격이 비슷함
- 자바의 클래스 구조를 더 조직화, 소스코드를 구현 시 효율을 높일 수 있음.
이름이 있는 지역 내부 클래스
- 메서드 내부에서 정의된 클래스, 지역 변수와 동일한 범위를 가짐
- 클래스의 이름이 명시되는 클래스
class Animal{
void performBehavior(){
class Brain{} //지역 내부 클래스 정의, 클래스가 선언된 메서드 블록 내에서만 사용 가능
}
}
이름이 없는 지역 내부 클래스
- 이름을 갖지 않는 내부 클래스
- new 예약어 뒤에 명시된 클래스가 기존의 클래스인 경우에 자동적으로 이 클래스의 자식 클래스가 됨
- 추상클래스의 객체를 내부 클래스 형태로 생성할 때 자주 사용
abstract class TV{ // 추상 클래스 정의
public abstract void powerOn();
public abstract void powerOff();
}
class AnonymousTest{
public static void watchTV(TV tv){ // 임의의 메서드 정의
tv.powerOn();
tv.powerOff();
}
public static void main(String[] args){
watchTV(new TV(){ // 무명 클래스를 이용하여 메서드를 호출하며
public void powerOn(){ // 추상 클래스의 기능을 사용할 수 있게 됨
System.out.println("TV---전원 켬")
}
public void powerOn(){
System.out.println("TV---전원 끔")
}
})
}
}
TV라는 추상 클래스의 객체를 내부 클래스 형태로 생성했기 때문에 실제로는 TV클래스를 상속한 내부 클래스가 만들어지게 됨.
■ 객체의 형변환
객체의 형변환 개요
- 부모 클래스 유형을 자식 클래스 유형으로 강제 형변환하는 경우 할당되는 인스턴스 유형에 따라 실행오류 발생
- instanceof 연산자를 사용해 생성된 객체가 class와 관계있는 type으로 만들어졌는지 확인(true/false)
class Employee{}
class MAnager extends Employee{}
public class InstanceOfTest{
public static void main(String args[]){
Manager m = new Manager();
Employee e = new Employee();
System.out.println(m instanceof Manager); // true
System.out.println(m instanceof Employee); // true
System.out.println(e instanceof Manager); // false
}
}
Manager객체 m은 Manager 타입임과 동시에 Employee 타입
Employee 객체는 Manager클래스가 가진 새로운 특징은 가지고 있지 않으므로 오직 Employee 타입
객체의 형변환
- 클래스의 형변환은 기본적으로 상속 관계까 아닌 클래스 사이에는 발생X
- 자식 클래스의 객체는 부모 타입의 참조 변수에 할당될 수 있음(Promotion)
class Employee{
String name;
int employeeNo;
int departmentNo;
public String getEmployeeInfo(){ ... }
}
class Manager extends Employee{
Employee[] employeeList; // 변수 추가
public String getManagerInfo(){ ... } // Overriding
}
- Employee 객체에 접근할 수 있는 경우
Employee e1 = new Employee();
// 변수 타입과 객체가 모두 Employee
// e1 참조변수를 통해 Emplyee 객체가 가지고 있는 변수, 메서드 모두 접근 가능
- Employee, Manager 객체에 모두 접근할 수 있는 묵시적 형변환인 경우
Manager m1 = new Manager();
// 변수 타입과 생성된 객체가 모드 Manager
// Employee객체를 상속받았으니 객체 생성시 Manager객체와 Employee객체 생성(promotion)
// Manager 객체 생성 시 Employee가 가진 속성이 메모리상에 로딩됨
// m1 참조변수를 통해 Emplyee,Manager 객체의 변수, 메서드 모두 접근 가능
- 생성된 객체와 참조하는 변수의 타입이 다른 경우
Employee e2 = new Manager();
// Manager는 Employee객체가 가진 변수와 메서드를 모두 가지고 있음
// 참조 변수의 타입이 Employee이므로, 접근 가능한 변수와 메서드는 Employee객체로 제한됨
// 문제가 있는 소스코드.
- Employee, Manager 객체에 모두 접근할 수 있는 Demotion인 경우
Manager m2 = (Manager)e2;
// Employee형의 참조 변수를 Manager형으로 명시적 형변환
// m2는 Manager타입이므로 e2가 참조하고 있던 Manager 객체의 모든 멤버필드에 접근가능(문제 없음.)
class TVFactory{
public TV getTV(String tvName){
if(tvName.equals("S사")){
return new S_TV();
} else if(tvName.equals("L사")){
return new L_TV();
}
else return null;
}
}
class abstractTVUser{
public static void main(String[] args){
TVFactory factory = new TVFactory();
TV tv1 = factory.getTV("S사");
TV tv2 = factory.getTV("L사");
}
}
모든 TV클래스들의 최상위 부모인 TV타입의 변수로 받을 수 있음
명령형 매개변수가 무엇인지에 따라 다른 TV객체가 실행되고, 소스코드의 수정은 발생되지 않음.
- 형변환에 참여한 서로 상속 관계에 있는 두 클래스 간에 동일한 이름의 변수가 존재하거나 메서드가 Overriding되어 있을 수 있으니 생성된 객체 변수를 통해 멤버에 접근할 때 주의해야 함
class Parent{
int num = 10;
void printNum(){
System.out.println(num);
}
}
class Child extends Parent{
int num = 20; // 동일한 이름의 변수가 부모 클래스에 존재하므로 상속X
void printNum(){ // 메소드 Overriding
System.out.println(num);
}
}
public class ObjectCastTest{
public static void main(String args[]){
Parent p = new Child(); // Promotion
p.printNum(); // 20 출력
System.out.println(p.num); // 10 출력
}
}
p.printNum() -> 메서드 호출은 할당되는 인스턴스에 의해 결정됨
p.num -> 변수에 대한 접근은 객체의 유형에 의해 결정됨
** ∴ 객체 참조 변수가 변수나 메서드를 참조하는 경우, 참조 관계를 결정하는 시간이 다르기 떄문에 나타나는 차이임.**
[참고] : SWExpertAcademy
'Etc.. > SWExpertAcademy' 카테고리의 다른 글
[Java Programming] 객체지향 핵심 원리(8) (0) | 2021.07.22 |
---|---|
[Java Programming] 객체지향 핵심 원리(7) (0) | 2021.07.22 |
[Java Programming] 객체지향 핵심 원리(5) (0) | 2021.07.17 |
[Java Programming] 객체지향 핵심 원리(4) (0) | 2021.07.17 |
[Java Programming] 객체지향 핵심 원리(3) (0) | 2021.07.17 |