[JAVA] 형변환(기본형변환/참조형변환[upcasting/downcasting])
@욕심쟁이
·2020. 9. 7. 17:38
예제1.형변환
- 어떤 데이터 타입을 다른 데이터 타입으로 변환하는것
- 기본(Primitive Type)형변환, 참조(Reference Type) 형변환
1. 기본형변환
- 기본데이터타입 8가지중 boolean을 제외한 나머지 7가지 타입끼리의 형변환
- 작은 타입에서 큰차입으로 변환시 자동형변환, 큰타입에서 작은타입으로 변환시 강제 형변환
- 강제형변환시 반드시 형변환 연산자를 사용하여 변환될 타입을 명시해야함
→ 단, 강제형변환 후에는 오버플로우가 발생할 위험이 있음
2. 참조 형변환(Reference형변환)
- 클래스간의 형변환(참조형 변수끼리의 형변환)
- 반드시 상속관계에서만 가능한 변환
- 자식(서브클래스)타입에서 부모(슈퍼클래스)타입으로 변환시 자동 형변환(업캐스팅)
- 부모(슈퍼클래스)에서 자식(서브클래스타입으로 변환시 강제 형변환(다운캐스팅)
3. 캐스팅이란?
- 캐스팅(casting)이란 타입을 변환하는 것을 말하며 형변환이라고도 한다. 자바의 상속 관계에 있는 부모와 자식 클래스 간에는 서로 간의 형변환이 가능하다.
- 부모 클래스인 상속 관계의 상위 클래스를 수퍼 클래스, 그리고 자식 클래스인 하위 클래스를 서브 클래스라고 정의
1) 업캐스팅(Up Casting)
- 자식 클래스(서브클래스)가 부모 클래스(슈퍼클래스)의 타입으로 캐스팅(상위(부모) 형으로 형변환)
- 슈퍼클래스 타입으로 서브클래스의 인스턴스를 참조하는 것
→ 서브클래스 인스턴스를 슈퍼클래스 타입으로 변환하는 것
- 묵시적(자동) 형변환이 일어남
- 참조가능한 영역이 축소됨(슈퍼클래스 타입에서 선언된 변수, 메서드만 접근 가능함)
- 하나의 슈퍼클래스 타입으로 여러 서브클래스 인스턴스의 공통된 멤버에 접근가능 하도록 해 줌!
- 업캐스팅시 자식클래스(서브클래스)에 정의되어있는 멤버변수, 메서드는 접근 불가
→ 자식클래스(서브클래스)에 오버라이드한 메서드를 통해
자식클래스(서브클래스)에 존재하는 멤버변수 접근, 조작가능
- 메소드 실행시 메소드가 실행되는 인스턴스의 변수를 사용함
<기본문법>
슈퍼클래스 타입 변수명 = 서브클래스 인스턴스;
2) 다운캐스팅(Down Casting)
- 부모 클래스(슈퍼클래스)가 자식 클래스(자식클래스)의 타입으로 캐스팅
- 서브클래스 타입으로 부모클래스의 인스턴스를 참조하는 것
→ 슈퍼클래스 인스턴스를 서브클래스 타입으로 변환하는 것
- 참조가능한 영역이 확대됨(서브클래스 타입에서 선언된 변수, 메서드에 모두 접근 가능함)
- 단, 실제 존재하지 않는 영역에 대한 접근 위험성이 발생함
- 묵시적(자동) 형변환이 일어나지않음
→ 반드시 형변환 연산자를 사용한 명시적(강제)형변환 필수!
- 명시적으로 형변환 후에도 실행 시점에서 오류발생 할 수 있음
→ 따라서, 일반적인 상황에서의 다운캐스팅은 허용되지않음(바로 다운캐스팅 X)
→ 이전에 이미 업팅캐스팅 된 인스턴스를 다시 다운캐스팅하는경우에만 안전하게 변환이 일어남
(업캐스팅 후 다운캐스팅만 가능)
<기본문법>
서브클래스 타입 변수명 = (서브클래스타입)슈퍼클래스 인스턴스;
예시)
public class Ex {
public static void main(String[] args) {
Child c = new Child(); //인스턴스생성 ->서브클래스에대한 인스턴스 생성
//서브클래스의 인스턴스c를 통해 접근가능한 메서드 :2개
c.childPrn();//자신읜 클래스에서 정의한 메서드 호출
c.parentPrn(); //상속받은 메서드 호출
//Parent()타입변수 p를 선언하여 Child클래스의 인스턴스 전달
Parent p = new Parent();
//서브클래스의 인스턴스 주소를 슈퍼클래스 타입 변수에 전달
p=c; //c가 가리키는 인스턴스주소와 p가 가리키는 인스턴스 주소가 같다.
// =>Child타입 -> Parent타입으로 변환됨(업캐스팅)
// =>업캐스팅은 자동 형변환이 가능하므로 형변환 연산자 생략 가능
//Parent타입 변수p를 통해 접근가능한 메서드 :1개
p.parentPrn(); //상속받은 메서드는 호출가능
//p.childPrn(); //(오류발생)서브클래스에서 정의한 메서드는 호출불가
//=>업캐스팅 후에는 참조영역에 대한 축소가 발생하므로
//상속된멤버외의 서브클래스에서 정의한 멤버는 접근 불가능 하게 됨
// 즉, 서브클래스의 멤버는 보이지 않게됨
System.out.println("p와 c의주소값이 값은가? " + (p == c));
System.out.println("=====================================");
Parent p2 = new Parent();
p2.parentPrn();
//다운캐스팅을 하게되면 참조영역에대한 확대가일어남
// Child c2 = p2;//오류 다운캐스팅은 자동 형변환이 지원되지 않음
// => 명시적(강제)형변환필요 = 형변환 연산자 사용
//Child c2 = (Child)p2; //서브클래스 타입을 명시하여강제형변환
//=>형변환 후에도 오류발생
//c2.childPrn();//parent 인스턴스에 존재하지 않는 메서드
//존재하지않는 영역에 대한 참조의 위험 때문에 다운캐스팅은 다종형변환이 지원되지 않으며 강제형변환을 하더라도 실행 시점에서 논리적오류가 받생
System.out.println("====================================");
//다운 캐스팅이 허용되는 경우
// Child c3 = new Child();//슈퍼클래스인스턴스 생성
// Parent p3 = c3;//업캐스팅
Parent p3 = new Child(); //위두문장을 하나로 결합가능 업캐스팅
// 슈퍼클래스(Parent)타입으로 접근 가능한 메서드:1개
p3.parentPrn();//상속받은 메서드
//이미 업캐스팅된 이스턴스를 다시 다운 캐스팅
//Child c3 = p3;// 명시적 형변환 필요
Child c3 = (Child)p3;//다운캐스팅(실행시 오류발생하지않음!)
//서브클래스타입으로 접근가능한 메서드:2개
c3.childPrn(); //서브클래스에서 정의한 메서드
c3.parentPrn(); //상속받은 메서드
//결론!
//이전에 이미 업캐스팅되어 참조영역이 축소되었던 인스턴스를
//다시 다운캐스팅을 통해 참조영역이 확대되면
//접근 범위에 아무런 문제가 없으므로 사용이 가능!
//따라서, 래퍼런스 형변환 시에는 상속관계를 고려하여야 한다!
// 알맞은 형변환 방식을 선택해서 변환해야한다!
}
}
class Parent{
//메서드 생성
public void parentPrn() {
System.out.println("슈퍼클래스의 parentPrn()메서드!");
}
}
class Child extends Parent{
//메서드 생성
public void childPrn() {
System.out.println("childPrn()메서드!");
}
}
3. 기본데이터타입 vs 참조데이터타입
- 두종류의 데이터 타입에 대한 값을 전달할 경우 해당값을 복사하여 전달
- 기본데이터 타입은 실제값의 복사가 일어남
→ 전달받은 (복사된)데이터(복사된 실제값)를 변경하더라고 원본 데이터에는 아무런 영향이 없다!
- 참조데이터타입은 주소값의 복사가 일어남
→ 전달받은 데이터(복사된 주소값)의 데이터(실제값)을 변경하면 변경된 내용이 동일하게 적용되어 있다!
(즉, 동일한 위치의 주소를 공유하므로 변경사항 공유)
예제)
public class Ex {
public static void main(String[] args) {
//기본데이터타입 vs 참조데이터타입
//-두종류의 데이터 타입을 메서드 호출시 전달할 경우 데이터를 복사하여 전달
int money = 100000; //기본형, 로컬변수(메인메서드안)
System.out.println("1. 변경전 money : "+money);
//primitiveChange()메서드를 호출하여 int형 변수 money의 값 전달
primitiveChange(money);//실제값(100000)을 복사하여 전달
//원본데이터는 아무런 영향을 받지 않는다!
System.out.println("2. 변경후 money : "+money);
System.out.println("=======================================");
MyMoney mm = new MyMoney();//MyMoney인스턴스 생성
System.out.println("3. 변경전 mm.money = "+ mm.money);
//primitiveChange()메서드를 호출하여 MyMoney인스턴스의 인스턴스변수인 기본데이터타입money파라미터로 전달
primitiveChange(mm.money);//인스턴스변수, 멤버변수이자 기본형변수 //mm의 주소값을 찾아가서 mymoney의 money의 100000;값을 들고온다.
//메서드에 전달하는 변수가 인스턴스 내의 변수라 하더라도
//기본데이터타입일 경우 값을 복사하여 전달함.
//따라서, 실제값을 복사하여 전달받은 메서드에서값을 변경하더라고
//원본 데이터는 아무런 영향을 받지않는다!
System.out.println("4. 변경 후 mm.money = " + mm.money);
System.out.println("=================================");
MyMoney mm2 = new MyMoney();
System.out.println("5. 변경 전 mm2.money = "+ mm2.money);
refernceChange(mm2);// 주소값을 복사하여 전달
//참조변수가 가진 주소값을 복사하여 메서드에 전달 한 뒤
//메서드 내에서 해당 참조변수에 접근하여 인스턴스 변수값을 변경하면
//원본 데이터가 있는 곳의 값을 변경하게 되므로 원본데이터도 영향을 받는다.
//=> 즉, 원본 데이터와 전달받은값이 가리키는 인스턴스가 동일하므로
//한곳에서 값을 변경하면 다른곳에서도 동일한 값을 사용하게됨
System.out.println("6. 변경 후 mm2.money = " + mm2.money);
}
public static void primitiveChange(int money) {
//외부로 부터 전달받은 데이터(실제값)를 변경
System.out.println("3-1. 메서드 호출 후 변경전 money = " + money);
money-=20000;
System.out.println("3-2. 메서드 호출 후 변경 후 money = " + money);
}
public static void refernceChange(MyMoney mm) {
//외부로부터 전달받은 데이터(주소값)에 접근하여
//해당 인스턴스 내의 멤버변수 값(실제값)을 변경
System.out.println("5-1. 메서드 호출 후 변경전 money = " + mm.money);
mm.money-=20000;
System.out.println("5-2. 메서드 호출 후 변경 후 money = " + mm.money);
}
}
class MyMoney{
int money = 1000000;
}
4. 다형성(Polymorphism)
- 하나의 클래스타입으로 여러 서브클래스타입 인스턴스를 참조하는것
- 업캐스팅을 의미함
→ 서브클래스에서 메서드 오버라이딩을 수행했을 때 슈퍼클래스타입으로 업캐스팅 후 메서드호출 시
오버라이딩된 메서드가 호출되어 코드는 동일하나 실행결과가 달라지게됨
- 다형성을 통해코드의 통일성을 향상시킬수있으나 해당 인스턴스의 상세 속성에 접근하려면 다운캐스팅을 통해 서브클래스 타입으로 변환한뒤 접근가능함
- 상속 : 변수, 메서드 오버라이드 가능
5. 업캐스팅시 오버라이딩
- 동적바인딩 : 코드상의 실행 할 메서드와 컴파일 후 실행 시점에서 실행되는메서드가 달라짐
- 오버라이딩 된 메서드가 존재할 경우 업캐스팅 후에도 오버라이딩 된 메서드가 호출됨
→ 즉 메서드호풀시 참조타입이 누준지는 중요하지않고 실제인스턴스가 누군지가 중요함
예제1.
public class Ex {
public static void main(String[] args) {
Truck t = new Truck();
t.dump();
t.speedDown();
t.speedUp();
Taxi tx = new Taxi();
tx.drop();
tx.lift();
tx.speedUp();
tx.speedDown();
System.out.println("=================업캐스팅=====================");
Car car = t;
car.speedDown();
car.speedUp();
System.out.println("=================다운캐스팅=====================");
t = (Truck)car;
t.dump();
t.speedDown();
t.speedUp();
car = tx;
//Taxi클래스에서 speedUp(),speedDown()메서드를 오버라이딩
//따라서 업캐스팅 후에도 오버라이딩된 taxi의메서드 호출
car.speedDown();
car.speedUp();
System.out.println("=================다운캐스팅=====================");
tx = (Taxi)car;
tx.drop();
tx.lift();
tx.speedDown();
tx.speedUp();
}
}
class Car {
public void speedUp() {
System.out.println("Car의 속력증가!");
}
public void speedDown() {
System.out.println("Car의 속력감소!");
}
}
class Truck extends Car{
public void dump() {
System.out.println("Trunck의 짐심기");
}
@Override
public void speedUp() {
System.out.println("트럭의 속력증가");
}
@Override
public void speedDown() {
System.out.println("트럭의 속력감소");
}
}
class Taxi extends Car{
public void lift() {
System.out.println("Taxi승객 승차!");
}
public void drop() {
System.out.println("Taxi승객 하차!");
}
@Override
public void speedUp() {
System.out.println("택시의 속력증가");
}
@Override
public void speedDown() {
System.out.println("택시의 속력감소");
}
}
예제2.
public class Test {
public static void main(String[] args) {
Circle c = new Circle();
c.rad=3;
c.draw();
Rectangle r = new Rectangle();
r.line1 = 10;
r.line2 = 20;
r.draw();
Triangle tg = new Triangle();
tg.a=10;
tg.b=20;
tg.c=30;
tg.draw();
System.out.println("=========================");
Shape s = c;
s.draw();
s = r;
s.draw();
s=tg;
s.draw();
System.out.println("================================");
shapeDraw(new Circle());
shapeDraw(new Rectangle());
shapeDraw(new Triangle());
}
public static void shapeDraw(Shape s) {
s.draw();
}
}
class Shape{
public void draw() {
System.out.println("도형 그리기");
}
}
class Circle extends Shape{
double rad;
@Override
public void draw() {
System.out.println("반지름"+rad+"를 이용하여 원그리기!");
}
}
class Rectangle extends Shape{
int line1, line2;
@Override
public void draw() {
System.out.println(line1+","+line2+"직선두개 사용하여 사각형 그리기!");
}
}
class Triangle extends Shape{
int a, b, c;
@Override
public void draw() {
System.out.println(a+","+b+","+c+"직선 세개 사용하여 삼각형 그리기!");
}
}
예제3.
public class Ex2 {
public static void main(String[] args) {
Parent2 p2 = new Parent2();
if(p2 instanceof Child2) {
//판별결과가true이면 무조건 CHild2차입으로 변환가능
//=>서브클래스를 이미 업캐스팅 해놓은 상태이므로 형변환 가능하다는 True가 리턴됨
System.out.println("p -> Child2타입으로 형변환 가능");
//Child2 c = p2;//자동형변환은 불가능한 관계이므로
Child2 c = (Child2)p2;
}else {
//판별결과가 true이면 어떠한 변환도 불가능
//->슈퍼클래스 타입 인스턴스이므로 다운 캐스팅 불가!
System.out.println("p -> Child2타입으로 형변환 불가능");
}
System.out.println("==============================================");
//SmartPhone인스턴스(sp)생성
SmartPhone 내폰 = new SmartPhone();
//내폰은 SmartPhone입니까?
if(내폰 instanceof SmartPhone) { //변수명 instanceof 타입
System.out.println("내폰은 SmartPhone이다.");
SmartPhone 동생폰 = 내폰;
동생폰.call();
동생폰.sms();
동생폰.kakaoTalk();
동생폰.youtube();
}
System.out.println("==============================================");
if(내폰 instanceof HandPhone) {
System.out.println("내폰은 HandPhone입니다.");
System.out.println("그러므로 HandPhone으로 형변환 가능하다!");
HandPhone 엄마폰= 내폰;
System.out.println("내폰은 HandPhone의 모든 기능을 포함한다!");
엄마폰.call();
엄마폰.sms();
}
System.out.println("==============================================");
HandPhone 엄마폰 = new HandPhone();
if(엄마폰 instanceof SmartPhone) {
System.out.println("엄마폰은 SmartPhone입니다.");
}else {
System.out.println("엄마폰은 SmartPhone이 아닙니다.");
}
System.out.println("==============================================");
HandPhone 엄마폰2 = new SmartPhone();
if(엄마폰2 instanceof SmartPhone) {
System.out.println("엄마폰2은 SmartPhone입니다.");
SmartPhone 내폰2 = (SmartPhone)엄마폰2;
System.out.println("엄마폰은 SmartPhone의 모든기능을 포함");
System.out.println("따라서 SmartPhone의 형변환 후에도");
System.out.println("정상적으로 SmartPhone의 모든 기능 사용가능");
내폰2.call();
내폰2.sms();
내폰2.kakaoTalk();
내폰2.youtube();
}else {
System.out.println("엄마폰은 SmartPhone이 아닙니다.");
}
}
}
class Parent2{
}
class Child2 extends Parent2{
}
//----------------------------------------------
class HandPhone{
String phoneNumber;
public void call() {
System.out.println("HandPhone의 전화기능!");
}
public void sms() {
System.out.println("HandPhone의 문자기능!");
}
}
class SmartPhone extends HandPhone{
String osName;
public void kakaoTalk() {
System.out.println("SmartPhone의 카카오 기능!");
}
public void youtube() {
System.out.println("SmartPhone의 유튜브 기능!");
}
}
'IT > JAVA' 카테고리의 다른 글
[JAVA]추상클래스와 추상메서드(Abstract) (0) | 2020.09.11 |
---|---|
[JAVA] INSTANCEOF 연산자 (0) | 2020.09.08 |
[JAVA] FINAL 키워드 (0) | 2020.09.02 |
[JAVA] 상속(Inheritance)[부모(슈퍼)클래스/ 자식(서브)클래스]1 (0) | 2020.08.31 |
[JAVA] this키워드 (0) | 2020.08.21 |