본문 바로가기

코딩공부일지/수업 review

자바: 다형성

Part1 다형성

1. 다형성이란:

- 자바에서는 한 타입의 참조변수로 여러 타입의 객체 참조 가능
- 부모클래스 타입의 참조변수(부모의 데이터타입으로)로 자식 클래스의 인스턴스(객체) 참조 가능

2. 다형성이 가능한 이유
- 자바의 상속: 자식클래스는 부모클래스로부터 필드, 메서드, 그리고 "데이터타입"을 물려받는다.

[1] 부모타입 자료형으로 부모 객체를 다루는 경우

	Parent p1 = new Parent();
	p1.printParent();


[2] 자식타입 자료형으로 자식객체를 다루는 경우
	Child1 c1 = new Child1 ();
	c1.printChild1(); 
       c1.printParent(); // 부모로부터 물려받은 메서드
  ==> 자식타입 객체로는 자기 자신, 그리고 부모의 메서드 모두 접근가능 
  
  
 [3] 부모타입 자료형으로 자식객체를 다루는 경우 (upcasting):  * 다형성 적용 *
 	 -> 자동형변환: 자식 -> 부모타입 형변환  
     
    
    Parent p2 = new Child1(); // 양변의 데이터타입이다름 ==> 자동형변환 (promotion)
 	Parent p2 = (Parent) new Child1(); // 자손->부모타입 형변환은 JVM자동형변환 가능
 
 
 
     -> 메모리에서 일어나는 일
      1) stack영역에 p2만큼의 공간(4byte)생성 (0x100으로 가는 주소를 저장하고 있음)
      2) heap 영역 0x100에 Child1() 만큼의 공간 생성  
            2-1) 부모인 Parent()만큼의 공간생성
            2-2) 부모 제외, 자식만큼 공간 추가생성
      3) Parent 형태를 데이터로 다루는 p2는 Child1() 공간 중에서 Parent 공간만큼만 활용가리킴

 
 
 
 
    * 부모타입 객체로 자식타입의 메소드 호출시 : 오류발생
    (Child1)p2.printChild(); 
     ---> 오류 : 연산자 우선순위에 의해서 p2.printChild()가 먼저 실행된 후 Child1으로 형변환

 


Parent p2 = new Child1();

3. ****주의 ***** down casting이 성립하는 조건

[4] 자식타입 자료형으로 부모객체를 다루는 경우 (down casting): 
	==>    promotion불가능, force casting시도시 일반적으로 runtime error발생

     	Child1 c2 = new Parent(); --> promotion 불가능     
   		Child1 c2 = (Child1)new Parent(); ==> force casting 시 runtime error 발생
        			 ClassCastException



	 ===>  단, 자식클래스 => up casting(부모타입 자료형으로 변환)
                         ==> down casting(다시 자식타입 자료형으로 변환) 
    						 인 경우 !
            force casting을 통해 강제형변환 후 사용가능
            
    
    Parent p = new Child1();  // 자손타입 객체를 자동형변환하여 부모타입 참조변수p에 저장
    Child1 c = (Child1)p; 
    // 부모타입으로 다운캐스팅된 자손객체 (p에 저장되어 있는 주소값)를 다시 자식타입으로 force casting
    	 
    ====> 이 경우 jvm은, (Child1)p의 태생인 child1 객체를 알고있다!


4. 다형성의 장점
- 부모타입 자료형으로 다양한 자식객체를 다룰수 있음

==> 하나의 배열 안에 여러개 타입 넣을 수 있음
==> 반복문 사용 가능
( 부모타입 배열에 다양한 자손타입 대입 가능 ==> 자손타입 객체가 부모타입 객체로 upcasting 되어 들어감)
====> 소스코드 감소로 효율성 증대

   < 다형성을 접목하기 전 배열 >
   
   Class child1 extends Parent.
   Class child2 extends Parent.
   
   Parent를 자손으로 하는 child1형 객체 2개, child2 객체 2개를 담으려면,
   child1[], child2[] 배열이 필요하다
   	   	   
	   Child1[] arr1 = new Child1[2];
			   
	   arr1[0] = new Child1(1,2,3);
	   arr1[1] = new Child1(4,5,6);

	   Child2[] arr2 = new Child2[2];
	   
	   arr2[0] = new Child2(7,8,9);
	   arr2[1] = new Child2(10,11,12);
		
	< 다형성을 접목한 후 배열 >  : 자손타입은 부모타입으로 자동 upcasting이 가능함
     ===> 한 종류의 배열로 여러 자손의 객체를 다룰 수 있음
    
    
	   Parent[] arr = new Parent[4];
	   arr[0] = new Child1(1,2,3);  // child1 타입 객체가 parent타입으로 promotion된 후 대입  
	   arr[1] = new Child1(2,3,4);	
	   arr[2] = new Child2(4,5,6);  // child2 타입 객체가 parent타입으로 promotion된 후 대입 
	   arr[3] = new Child2(3,4,5);
				
	   
	   for (int i=0; i < arr.length; i++) {
		   arr[i].printParent();
	   }
	   
       ---------------------------------------------------------
	 printChild1, 2를 호출하려면 ==> child형으로 강제형변환 후 사용
     하단의 arr[0]~ arr[3]은 태생이 child형이었는데 upcasting되어 arr배열에 저장되어 있었던 것
     casting 연산자로 다시 downcasting하여 child1, child2가 가지고 있는 메소드 사용 가능
     
     
	   ((Child1)arr[0]).printChild1();
	   ((Child2)arr[1]).printChild2();
	   ((Child2)arr[2]).printChild2();
	   ((Child1)arr[3]).printChild1();
	   
	    instanceof 연산자 => t/f반환  if (a instanceof b) {    }
	    현재 객체가 실질적으로 어떤 클래스타입을 참조하는지 확인할 때 사용
	   
	   for (int i=0; i < arr.length ; i++) {
	   
		   if(arr[i] instanceof Child1) {
			   ((Child1)arr[i]).printChild1();
		   } else {
			   ((Child2)arr[i]).printChild2();
		   } 
	
	   }
	   for (int i=0; i < arr.length ; i++) {
		   arr[i].print();
public class ElectronicsController1 {	

		// 필드부
		private Tv tv;
		private SmartPhone sp;
		private AirPod ap;
		
		
		// 생성자부 생략 ===> jvm이 기본생성자 자동 생성
		
		// 메서드부
		// 재고를 추가해주는 기능 --> 오버로딩 (조건: 매개변수의 자료형 or 개수 or 순서가 다르다)
        
		public void insert(Tv tv) { //setter역할을 하는 메서드
			this.tv =tv;
		} 
		public void insert(SmartPhone sp) {
			this.sp =sp;
		}
		public void insert(AirPod ap) {
			this.ap =ap;
		}
		
		public Tv selectTv() {  // getter역할을 하는 메서드
			return tv;
		}
		
		public SmartPhone selectSmartPhone () { 
			return sp;
		}
		// 
		
		public AirPod selectAirPod () {
			return ap;
		} 
		
		
	}
public class Electronics {

	// 필드부
	private String brand;
	private String name;
	private int price;
	// 생성자부
	public Electronics() {
		super();
	}
	public Electronics(String brand, String name, int price) {
		super();
		this.brand = brand;
		this.name = name;
		this.price = price;
	}

	// 메서드부
	
getter-setter
toString생략

 

ublic class SmartPhone extends Electronics {
    // 필드부 없음
    // 생성자부
	private String phoneNumber;
	public SmartPhone() {
		super();
	}
	public SmartPhone(String brand, String name, int price, String phoneNumber) { // 매개변수 순서는 부모의 생성자 매개변수와 동일하게! 바꾸지 말것
		super(name, brand, price);
		this.phoneNumber = phoneNumber;
	}
   
   
   // getter - setter
	public String getPhoneNumber() {
		return phoneNumber;
	}
	public void setPhoneNumber(String phoneNumber) {
		this.phoneNumber = phoneNumber;
	}
	
    // toString
	@Override
	public String toString() {
		return super.toString() + ", phoneNumber : "+phoneNumber;
	}	
}
class Run {
	
	public static void main(String[] args) {
	
    
    
    1. 다형성을 적용 하지 않았을 경우
	   객체 생성: 재고를 담을 필드들이 메모리영역에 올라감
		
	ElectronicsController1 ec1 = new ElectronicsController1();
	
    ***이해가 어려웠던 부분 
	ec1.insert(new Tv("LG", "스탠드형티비", 3000000, false)); 
    // new Tv()의 주소값을 매개변수로 넘기는 것!
	ec1.insert(new SmartPhone("베가", "lg",2000000, "010-1234-5678"));
	ec1.insert(new AirPod("베가", "lg",2000000, true));
	
	Tv t = ec1.selectTv();
	SmartPhone s = ec1.selectSmartPhone();
	AirPod a = ec1.selectAirPod();
	
	System.out.println(t.toString()); // toString은 출력문 안에 있을 때에는 생략이 가능하다
	System.out.println(s);
	System.out.println(a);
	
	
	
 2. 다형성을 적용했을 경우
	
	ElectronicsController2 ec2 = new ElectronicsController2();
	
	ec2.insert(new Tv("LG", "스탠드형티비", 3000000, false), 0); 
	ec2.insert(new SmartPhone("베가", "lg",2000000, "010-1234-5678"), 1);
	ec2.insert(new AirPod("베가", "lg",2000000, true), 2);
	
	Tv tv =(Tv) ec2.select(0);
	SmartPhone sp =(SmartPhone) ec2.select(1);
	AirPod ap =(AirPod) ec2.select(2);
	
	System.out.println(tv);
	System.out.println(sp);
	System.out.println(ap);
	
	
	// 재고 전체 출력
	Electronics[] elec = ec2.selectAll(); // 얕은복사 ==> 주소값 받아옴
	for (int i =0; i< elec.length; i++) {
		System.out.println(elec[i]);
		
	}
	
	
}


<기타 배운 것>

A.contains(B)  
==> A 문자열에 B문자열이 포함되어 있는지 여부를,
    boolean(t/f)타입으로 반환하는 메소드
    boolean타입이므로 조건식 사용이 가능하다
    
    A.equals(B)와 비슷하게 활용됨
    (A문자열의 내용과 B문자열 내용이 동일한지 여부를 boolean으로 반환하는 메서드)





어려웠던 부분:
- downcasting이 가능한 경우
- 동적바인딩, 정적바인딩 다시보기
- ec1.insert(new Tv("LG", "스탠드형티비", 3000000, false));
// new Tv()의 주소값을 매개변수로 넘기는 것