다형성/ 클래스타입 변환/ 자동 타입 변환/ 강제 타입 변환/ instanceof
예전에 자동타입변환이랑 캐스팅(강제타입변환) 배웠었다.
그때는 변수 타입이었는데
클래스도 이러한 타입 변환이 있다!
또한 '다형성'이라는 개념도 배울 건데,
'앞서 배운 재정의 + 이번에 배울 타입 변환'을 이용하면
객체지향 프로그래밍의 매우 중요한 특징인 '다형성'을 구현할 수 있다.
<다형성>
어떤 객체를 이용하느냐에 따라 다양한 실행결과가 나오고, 성능이 달라진다.
다형성을 구현하려면 '메소드 재정의'와 '타입 변환' 필요하다.
그럼 이제 타입 변환을 공부하자
자동 타입 변환 (promotion)
ㅇ클래스에도 타입변환이 있다. --> 클래스의 변환 : 상속 관계에 있는 클래스 사이에서 발생!!
-->자식은 부모타입으로 자동 타입변환이 가능.
<이거한번보자>
Cat cat = new Cat();
Animal animal = cat;
(또는 Animal animal = new Cat();)
여기서 cat을 animal에 대입하면 자동 타입 변환이 일어난다.
부모 클래스 타입으로!
주의할 점 : cat과 animal 변수는 '타입만 다르고' '동일한 객체 Cat을 참조한다.'
즉, cat == animal은 true다.
++바로 위 부모가 아니어도 상위 타입이면 상위 타입으로 자동 타입 변환이 일어난다. (상속 관계여야 함)
주의점: 부모타입으로 자동 타입변환 되면 --> 부모 클래스에 선언된 필드와 메소드만 접근 가능.
접근 가능한 멤버는 부모클래스멤버로만 한정..! (변수가 자식 객체를 참조하더라도 그렇다.)
(자식클래스에 있는 메소드 못부른다!!!)
여기서 중요한 예외가 있다 --> 만약 부모 메소드가 자식 클래스에서 재정의되었다면? 자동 타입변환 되었더라도 자식 클래스의 메소드가 대신 호출된다. (--> 곧 배울 다형성과 관련이 있음. 매우 중요함!!!)
<<익히기 : 자동 타입 변환 후의 멤버 접근>>
필드의 다형성
부품(객체)은 교체될 줄 알아야 한다.
또한 새로 교체되면, 사용 방법은 동일하지만 실행 결과는 우수하게 나와야 할 것이다.
자동 타입 변환.. 왜 부모 타입으로 변환해서 사용할까?
필드 타입을 부모 타입으로 선언하고 나서
다양한 자식 객체들이 저장되고 자식 객체를 이용할 수 있기 때문에
필드 사용 결과가 달라질 수 있다. --> 이것이 필드의 다형성이다.
ㅇ부모를 상속하는 자식 클래스는 부모의 필드, 메소드를 가지괴 있음 ->사용 방법 동일
ㅇ자식은 부모 메소드를 재정의해서 더 우수한 실행 결과 나오게끔 할 수 있음.
ㅇ자식 타입을 부모 타입으로 변환 가능
-->이 3가지가 다형성 구현할 수 있는 기술적 조건이다.
<<필드의 다형성을 코드로 살펴보자>>
'자식은 부모 타입으로 자동 타입변환 된다.'
'Car객체에 Tire의 자식객체가 저장되더라도, Tire클래스에 선언된 것들만 이용하므로 문제 없다'
'자식 클래스는 어차피 부모 클래스 멤버를 가지고 있다!'
+
Car 객체에 run()메소드가 있고
run() 메소드는 각 Tire객체의 roll() 메소드를 다음처럼 호출한다고 하자.
void run() {
frontLeftTire.roll();
frontRightTire.roll();
backLeftTire.roll();
frontRightTire.roll();
}
위에서 frontRightTire와 backLeftTire를 교체했었다.
교체 전에는 Tire객체의 roll() 메소드가 호출된다.
자식클래스로 교체 후에는 자식클래스에서 roll() 메소드를 재정의하고 있을 때
교체 이후에는 자식 클래스의 roll() 메소드가 호출된다. --> 즉 실행 결과가 달라진다.
-->자동 타입 변환을 이용해서 Tire 필드값을 교체했다.
이 때 Car의 run() 메소드 블록 안에 부모객체의 메소드를 사용하고 있을 때,
어어 나 자식객체 넣었는데 어캐..하면서 코드를 수정할 필요 없이
다양한 roll()메소드의 실행 결과를 얻게 된다.
(자식객체에서 메소드 재정의함에 따름)
이것이 필드의 다형성이다.
부모 타입 필드에 자식클래스 객체 넣어도 아무 문제 없고,
어차피 자동타입변환도 되고 자식클래스가 부모클래스 정보를 가지고 있는 상태니까
자식클래스에서 매소드 재정의 등을 통해 다양한 실행 결과를 가져올 수 있다.
다형성
ㅡ객체 사용 방법은 동일하지만 실행결과가 다양하게 나오는 성질.
ㅡ구현 기술: 메소드 재정의와 타입 변환
ㅡ자식 객체가 재정의된 메소드를 가지고 있을 경우, 부모 타입으로 자동 타입 변환 후에 메소드를 호출하면 재정의된 자식 메소드가 호출되면서 다양한 실행결과를 가져올 수 있다.
위에서 뚝뚝 끊어서 학습한 내용을 이제
실제 작성해보자.
Tire 클래스, Car클래스(Tire를 부품으로 가짐), HankookTire와 KumhoTire 클래스, CarExample 클래스
(와.. 요런 느낌 몬가 되게 좋다)
Tire 클래스
Car클래스
Tire객체 각 4개를 생성했고, 각각의 객체에는 각각의 Tire필드가 속해있다.
run()메소드는 Tire객체의 roll()메소드를 사용하고 있으며, roll()메소드는 논리값을 리턴했었다.
논리값 false면 펑크인 상황이었다. 그러니까 각 타이어.roo()이 false일때 stop() 메소드를 실행한다.
HankookTire 클래스 - 자식 클래스
KumhoTire 클래스 - 자식 클래스
이 자식 클래스들에서는 부모클래스인 Tire클래스에 있는 roll()메소드를 재정의하고 있다.
재정의하는 이유는 상황에 맞는 다른 내용을 출력하기 위함이다.
그냥 Tire가 펑크났을 때 HankookTire나 KumhoTire로 교체를 하고 나서
더이상 'Tire'가 아닌 HankookTire or KumhoTire의 정보로 진행한다.
(교체할 때, 이전에 만들었던 Tire 객체에다가 자식 클래스인 Hankook/KumhoTire 객체를 생성자로 생성해서 대입을 한다. 새로운 객체를 생성하여 필드값도 초기화되고, 이 자식 객체는 부모 객체를 상속하고 있기 때문에 클래스 멤버는 부모 클래스 멤버를 따른다.)
실행클래스
Car클래스의 run() 메소드는 펑크난 타이어 위치를 리턴해주는 거였다. 이에 따라 스위치문을 작성한다.
이 때 펑크가 났다고 하는 것은 Tire클래스의 roll() 메소드가 false를 리턴하는 것이다.
run() 메소드 블록 안에는 roll() 메소드가 존재하기 때문에
roll() 메소드가 한 번 실행됨에 따라 accumulatedRotation이 1증가하고, 타이어 상황을 출력한다.
때문에 for문이 5회 반복되면서 타이어 상황을 알려주고, 만약 펑크나는 상황에 이르렀다면 스위치문에 걸려서 타이어를 교체하게 된다.
-->
뭔가 메소드 안에 메소드가 있어서
프로그램을 작성할 때 어떤 메소드가 어떤 리턴을 하고 어떤 정보를 가지고 있는지 확실히 인지하면서 작성해야겠다.
이번 연습으로 부모클래스와 자식클래스의 연관성을 익혔다. 메소드를 사용하는 느낌도 괜찮았다.
자식객체를 이런 상황에서 새로 선언한다는 것과, 객체를 새로 선언했으므로 필드값도 초기화된다는 이 상황이 중요하게 느껴졌다. 자식클래스에서 메소드를 재정의해서 이런 목적으로 사용하구나도 알고 간다.
(자식 필드는 부모 클래스의 필드를 상속받고 있으므로 편리했다.)
(자식 객체를 부모 객체에 넣을 때, 별다른 조치가 필요하지 않았다. 자동 타입 변환!!)
매개변수의 다형성
매개변수에도 부모 객체와 자식 객체를 넣을 수 있다.
원래 매개변수에 부모 객체가 선언되어 있을 때,
자식 객체가 매개값으로 사용되면 '자동타입변환이 일어나' 부모 객체를 따라간다.
"매개변수 타입이 클래스일 때,
그 부모 객체의 자식객체도 매개값으로 사용할 수 있다"
-->
"매개값의 자동 타입 변환 + 메소드 재정의" 를 통해서
매개변수의 다형성도 구현할 수 있다~.~!!
강제 타입 변환 casting
!!!헉
부모 타입을 자식 타입으로 변환하는 것..!
그치만.. 이럴때만 가능하다.
'자식타입이 부모타입으로 자동타입 변환되고 나서 다시 자식타입으로 강제변환하기'
Parent parent = new Child(); //자식->부모 자동타입변환
Child child = (Child) parent; //부모->자식 강제타입변환
필요성
>자식이 부모로 자동타입변환하면 부모에 선언된 필드, 메소드만 사용 가능하다는 제약이 있음
>그래서 자식에 선언된 필드와 메소드를 꼭 이용해야 한다면
>강제타입변환한 후 자식에 있는 멤버 이용하면 된다.
아래 코드에서는
메소드3과 필드2가 자식클래스에만 선언되어 있어서
캐스팅을 한 뒤 사용해야 하는 상황을 볼 것이다.
+처음부터 부모 타입으로 생성된 객체는 자식타입으로 변환할 수 없다.
Parent parent = new Parent();
Child child = (Child) parent; //불가능
부모변수가 참조하는 객체가 자식객체인 상황에서만 캐스팅이 가능하다.
(ㅡ부모타입인 부모변수는 자식객체를 참조한다.)
(ㅡ자동타입변환이 일어난 후에 캐스팅 가능)
그럼 부모 변수가
'부모 객체를 참조하는지, 자식 객체를 참조하는지'를 먼저 알아야
캐스팅을 하든 말든 할 수 있지 않을까??
객체 타입 확인 : instanceof 연산자
어떤 객체가 어떤 클래스의 인스턴스인지 확인하기 위한 연산자 : instanceof
논리값을 리턴한다.
boolean result = 좌항(객체) instanceof 우항(타입)
주로 매개값의 타입을 조사할 때 사용한다.
메소드 내에서 강제타입변환이 필요하면.. "매개값에 넣는 객체가 어떤 객체인지" 이 연산자를 통해 확인해야 한다.
매개변수가 참조하는 객체가 자식객체인지 부모객체인지 알아야 강제타입변환을 할 수 있다.
강제 타입 변환이 옳지 않으면 ClassCastException이 발생한다.
instanceof 연산자는 다음처럼 사용할 수 있다.
public void method(Parent parent) {
if(parent instanceof Child) {
Child child = (Child) parent;
}
}
<<intanceof 연산자와 강제타입변환 개념 익히기>>
확인문제
02.
SnowTire 객체 (자식)을 생성했고,
Tire 타입의 tire 부모 변수에 이 SnowTire객체를 대입했다.
그리고 snowTire변수가 참조하는 객체인 SnowTire객체의 메소드 run() 실행
그리고 tire 변수에 snowTire변수를 대입함으로써 이 tire변수가 참조하는 객체인 SnowTire객체의 메소드 run() 실행
따라서 출력결과는
스노우 타이어가 굴러갑니다.
스노우 타이어가 굴러갑니다.
05.
멤버 로그인
A 로그인
(setService()의 매개타입은 MemberService고, 이어서 필드값이 변경된다. 첫번째 출력 과정은 매개값에 MemberService 객체가 사용되고 있으므로, 타입 변환이 일어나지 않는다. 반면 두번째 출력 과정에서는 MemberService의 자식 객체인 AService가 매개값으로 들어가 있기 때문에 부모클래스 타입으로 자동 타입 변환이 일어난다. 이 때, 부모 클래스의 메소드 login()은 자식 클래스에서 메소드 재정의가 되고 있다. 자동 타입 변환될 때 재정의된 메소드를 이용하게 되므로, AService클래스의 login()메소드를 실행하게 된다.)
'JAVA' 카테고리의 다른 글
[혼자 공부하는 자바] 7/11 8-1 [인터페이스] [추상 메소드] [실체 메소드] (0) | 2020.07.11 |
---|---|
[혼자 공부하는 자바] 7/11 07-3 [추상 클래스] [실체 클래스] [메소드 재정의] (0) | 2020.07.11 |
[혼자 공부하는 자바] 7/09 07-1 [상속] [자식클래스] [부모클래스] [오버라이딩] [메소드 재정의] (0) | 2020.07.10 |
[혼자 공부하는 자바] 7/08 06-6 [패키지와 접근 제한자] (0) | 2020.07.08 |
[혼자 공부하는 자바] 7/08 06-5 [인스턴스 멤버와 정적 멤버] [static] / [private] [접근제한자] / [final 필드] [상수] (0) | 2020.07.07 |