본문 바로가기

언어/Java

메서드 오버라이딩

삼각형모양 = 재정의되었다

cf) 메서드 오버로드 조건
     - 메서드 이름 동일
     -  매개변수갯수 다르거나, 순서 다르거나, 자료형이 다르다

 

※ 메서드 오버라이딩 조건
     - 상속관계
     - 메서드이름 동일, 매개변수 동일, 반환형 동일해야한다.
     - 접근범위는 부모메서드 접근범위보다  <= 자식메서드 접근범위가 더 넓거나 같아야 한다.

class Parent { class Child extends Parent{
    void m(){  }     //void m(){ }  //protected void m(){ } //public void m(){ } // OK
}     //private void m(){ } // ERROR
  }

abstract class Shape{ // 컴파일시 class Shape extends Object{}  로 바뀜.                               
//	private double area;
	protected double area;
	double getArea() {
		return area;
	}
		
//	void makeArea() {
//	
//	}
	
	abstract void makeArea();
	
	public String toString() {
		return "도형의 면적은 " + area + "입니다";
	}	
}
class Triangle extends Shape{
	// void makearea 하면 에러남
	void makeArea() {
		
	}
}
class Circle extends Shape { // shape로부터 확장된/상속받는 class이다. 자식 쪽에서 부모를 결정한다.
			// 부모로부터 상속받으면 자식 class가 간결해짐. 재사용성이 높아짐.
	
	private int radius;
	
	Circle(int radius) {
		this.radius = radius;
	}
	int getRadius() {
		return radius;
	}
		
	void makeArea() {
		area = 3.14 * radius * radius;
	}
	
	public String toString() {
		return "반지름이 " + radius + "인 원 " + super.toString();	//super.toString 부모의 메소드 불러옴, 형제들 사이는 전혀 상관없다
	}
}
class Rectangle extends Shape {
	private int width, length;
	
	Rectangle(int width, int length) {
		this.width = width;
		this.length = length;
	}
	
	void makeArea() {
		area = width * length;
	}
	
	public String toString() {
		return "가로 " + width + ", 세로 " + length + "인 사각형 " + super.toString();
	}
}
/*
public class ShapeTest {	//=public class ShapeTest extends Object{}
	//실행시에 필요한 로드된 클래스 갯수 : public, object, string[], circle, system, rectangle 6개
	public static void main(String[] args) {
		Circle c = new Circle(5); // 반지름이 5인 원객체
		System.out.println(c.getRadius());	//5
		c.makeArea(); //원의 면적을 계산한다.
		System.out.println(c.getArea()); //면적값 X.XXXX
		
		Rectangle r = new Rectangle(3, 4); //가로3, 세로4인 사각형객체
		r.makeArea(); // 사각형의 면적을 계산한다
		System.out.println(r.getArea()); // 면적값 12.0
	}
}
*/
/*
public class ShapeTest {
	public static void main(String[] args) {
		Shape[] shapes = new Shape[5];
		shapes[0] = new Circle(5);	//upcasting
		shapes[1] = new Rectangle(3, 4); //upcasting
		
		for(int i = 0; i<2; i++) {
//			shapes[i].makeArea();	이게 불가능해서 아래처럼 downcasting(자식타입으로 강제형변환)
			
//			Rectangle 객체도 있어서 instanceof 안하고 그냥 이렇게만 쓰면
//			ClassCastException 발생함
//			Circle c = (Circle)shapes[i];
//			c.makeArea();
//			System.out.println(c.getArea());
			
			// 무조건 downcasting 할 수 있는거 X
			if(shapes[i] instanceof Circle) {	// downcasting전에 꼭 물어보기
            		//instanceof 원래 객체가 Circle type 객체니? 
				Circle c = (Circle)shapes[i];
				c.makeArea();
				System.out.println(c.getArea());
			}else if(shapes[i] instanceof Rectangle) {
				Rectangle r = (Rectangle)shapes[i];
				r.makeArea();
				System.out.println(r.getArea());
			}
			
		}
	}
}
*/
// 사용자코드
// 부모에 메서드만 있으면 재정의되서 위에 처럼 객체 확인
// 강제형변환 안하고 아래처럼 간단하게 작성 가능
public class ShapeTest {
	public static void main(String[] args) {
		Shape[] shapes = new Shape[5];	// 방을 생성한거지 Shape 객체를 생성한건 아님.
		shapes[0] = new Circle(5);	//upcasting
		shapes[1] = new Rectangle(3, 4); //upcasting
		
		System.out.println(shapes[4]); //shapes[4].toString()가 자동 호출 null
		
		for(int i = 0; i<2; i++) {
			shapes[i].makeArea();						
			System.out.println(shapes[i].getArea());	
			System.out.println(shapes[i].toString());	// Circle@200a570f, Rectangle@16b3fc9e
			System.out.println(shapes[i]);	//shapes[i].toString()가 자동 호출됨
				//= System.out.println(shapes[i]); 알아서 인자의 toString 메서드가 자동 호출, shapes[i].toString()
			// Shape class에 빈 makeArea 만들었음, shape class에 makeArea 있는지 컴파일 시 확인하고 실행됨
			// 실행할 때는 그 shape로 부터 상속받은 실제 객체 circle/rectangle에 오버라이딩된 메서드 호출된다.
			// 0번 인덱스가 참조하는 객체 Circle 타입, 1번 인덱스 Rectangle
			// 0번 인덱스 자료형이 shape 타입이니까 부모인 shape 영역만 참조 가능, 자식영역에서 makeArea가 재정의되서 덮어쓰기
			// 오버라이딩 된 거 메서드는 계속 유지되서 사용됨
			// 인덱스에 따라 makeArea 각자꺼 가져와서 사용, 이게 다형성
			
			
			
			// 도형의 면적은 78.5입니다. //도형의 면적은 12.0입니다가 나오도록 toString을 오버라이딩필요, 이 경우 부모가 object, 자식이 shape가 됨.
			/* shape에 이거 넣으면 됨.
			public String toString() {
				return "도형의 면적은 " + area + "입니다";
			}
			*/
			
			// 반지름이 5인 원 도형의 면적은 78.5입니다. //가로3, 세로4인 사각형 도형의 면적은 12.0입니다가 나오도록 Circle과 Rectangle에 오버라이딩
		}
	}
}

 

 

 

※ System.out.println(shapes[i]); 이건 아래 10개 중 9째인 부모인 Object type의 매개변수 있는게 출력됨
PrintStream.println(), PrintStream.println(boolean), PrintStream.println(char), PrintStream.println(char[]), PrintStream.println(double), PrintStream.println(float), PrintStream.println(int), PrintStream.println(long), PrintStream.println(java.lang.Object), PrintStream.println(java.lang.String)

public void println​(Object x) {
    String.valueOf(x);
}

public static String valueOf​(Object obj) {
    if(obj == null) {
        return "null";
    else {
        return obj.toString.toString();
    }
}

 

shapes[0].mc();
shapes[1].mr();
→ compile 에러 남. shpaes 클래스에 mc,mr메서드가 없기 때문

쓰고 싶으면
Circle c = (Circle)shapes[0];
c.mc();
이런식으로 downcasting 해야함

m(); void 만들어두면 알아서 오버라이딩된 메서드 호출됨
shapes[0].m();

shapes[1].m();
까다롭지만 오버라이딩 안하면 사용자가 쓰기 불편하다. 재사용성이 떨어진다