본문 바로가기
JAVA

2022-10-17 객체지향언어 - 다형성. 추상클래스, 제어자 fanal

by HTT 2022. 10. 18.

다형성

 


다형성을 이해하기 위해 알아야 할 것

 

1. 상속

2. 오버라이딩

3. 추상클래스(abstract, interface)

4. 객체의 형변환

 

- 조상클래스 타입의 참조변수로 자손클래스의 인스턴스를 참조할 수 있도록 하는 것.

  반대로 자손타입의 참조변수로 조상타입의 인스턴스를 참조할 수는 없다.

 

- 참조변수가 사용할 수 있는 멤버의 개수는 인스턴스 멤버 개수보다 같거나 적어야 한다.

  (자식이7개, 조상이5개이면 안 된다는 뜻)

 

 

 

 


참조변수의 형변환

 


: 사용할 수 있는 멤버의 갯수를 조절하는 것.

 

- 서로 상속관계에 있는 클래스사이에서만 가능하다.

  => 자손타입의 참조변수를 조상타입의 참조변수로, 조상타입의 참조변수를 자손타입의 참조변수로의 형변환만 가능

  => 부모객체에 자식객체를 담을 수 있다(부모 <----- 자식).

       자식객체에 부모객체를 담을 수 없다(자식 <--x-- 자식).

 

//객체의 형변환
class Super{
	String name = "페데리코";
	public void display() {
		System.out.println("super의 display");
	}
}

class Sub extends Super{
	String name = "bts";
	public void display() {
	System.out.println("sub의 display");
	}
	public void test() {
		System.out.println("sub의 test");
	}
}
class Sub2 {
	
}
public class RefTypeCstingTest {

	public static void main(String[] args) {
		System.out.println("1. Super타입의 참조변수로 Super객체를 접근");
		Super obj1 = new Super();
		obj1.display();
		System.out.println(obj1.name);
		System.out.println("*************************************");
		System.out.println("1. Sub타입의 참조변수로 Sub객체를 접근");
		Sub obj2 = new Sub();
		System.out.println(obj2.name);
		obj2.display(); //상속관계에서 오버라이딩된 메소드가 있는 경우 오버라이딩된 메소드가 우선 실행됨
		
		
		System.out.println("*************************************");
		System.out.println("3. Super타입의 참조변수로 Sub객체를 접근");
		Super obj3 = new Sub();  // Sub를 Super타입으로 바꾸겠다는 뜻. 변수타입을 Super로 바꾸는 것,
                                 // 하지만 Sub, Super 둘 다 갖고 있음
		                         // 뒤에 있는 애가 앞에 있는 애보다 커야 한다(내용물이).
		
		System.out.println(obj3.name); //Super타입이라 "페데리코" 출력됨
		// obj3이 참조하는 객체가 Sub타입인 경우 명시적 캐스팅가능
		((Sub)obj3).test();
		obj3.display();
		
		
		System.out.println("*************************************");
		System.out.println("4. Sub타입의 참조변수로 Super객체를 접근");
		//Sub obj4 = new Super();  // 실제 사용되는 변수타입에 대한 정보가 생성되는 객체에없기
                                   // 때문에 객체를 참조할 수 없다.
		
		
		System.out.println("*************************************");
		System.out.println("5. Sub변수 = Super객체를 참조하는 변수");
		// 명시적으로 캐스팅하면 컴파일러는 속일 수 있으나(상속관계에 있어야만 속는다.)
        // obj1이 참조하는 객체가 Super이므로 런타임 시 오류가 발생한다.
		//Sub obj5 = (Sub)obj1;  //Sub obj5 = new Super();
		
		System.out.println("6. Sub변수 = Sub객체를 참조하는 Super 변수 ------ 가능함");
		System.out.println("*************************************");
		Sub obj6 = (Sub)obj3;
		// obj3이 참조하는 객체가 Sub타입이면 true를 반환
		// obj3이 Sub타입이 아니면 false를 반환
		if(obj3 instanceof Sub) {
			System.out.println("Sub타입");
		}else {
			System.out.println("Sub타입아님");
		}
		
		
		if(obj1 instanceof Sub) {
			System.out.println("Sub타입");
		}else {
			System.out.println("Sub타입아님");
		}
	}
}

 

// 변수끼리 있을 때
Sub obj6 = (Sub)obj3;
---
 ㄴ> 얘가 기준!!

* obj3의 타입을 Sub로 바꾸겠다는 뜻

  Sub타입으로 바꾸려면 (왼쪽의)Sub가 Super, Sub 둘 다 갖고 있어야 함

  => obj3를 Sub타입으로 바꿔서 obj6(참조변수)에 넣겠다는 뜻

 

* Super 변수명 = new 객체(<- Super의자손들)

     ㄴ> Super만 담고 있으면 됨

  Sub 변수명 = new 객체

     ㄴ> Sub, Super 둘 다 담고 있어야 함(Sub는 Super를 상속받았기 때문에)

 

 

 

 


추상클래스

 


=> 객체를 생성할 수 없는 클래스. 즉, 모든 구현되어 있지 않은 클래스(미완성된 클래스)
     내용이 구현되지 않은 메소드를 포함하고 있는 클래스를 추상클래스라 한다.

추상메소드(body가 없는 메소드)

 


1) 추상메소드를 선언하는 방법
▶  접근제어자 abstract 리턴타입 메소드명(매개변수list...);


2) 추상클래스의 특징
   - 일반메소드와 추상메소드를 모두 정의할 수 있다.
   - 추상메소드가 하나라도 정의되어 있으면 클래스 선언부에 abstract를 추가한다.
   - 추상클래스는 객체생성을 할 수 없는 클래스
   - 추상클래스를 상속받는 클래스는 extends하면 에러가 발생
 => SuA가 SuperA를 상속받는다. SuperA의 모든 멤버가 SubA의 소유 -> 따라서 SubA도 추상메소드를 갖는 추상클래스가 된다.


 <에러처리>
 * SubA앞에 abstract을 추가한다.
 * 상속받은 abstract메소드를 구현한다(메소드의 body를 정의한다.).

  - abstract클래스는 상위클래스로 만들기 위해서 사용(상위클래스로 정의되고 타입으로 사용하기 위해서 사용)
  - abstract메소드는 하위클래스에서 반드시 오버라이딩되어야 한다는 것을 문법적으로 제시

 

abstract class SuperA{
	public void test() {  //일반메소드
		System.out.println("test");
	}
	public abstract void display(); //추상메소드
}
class SubA extends SuperA{
	@Override
	public void display() {
		System.out.println("display");
	}
}

public class AbstractTest {

	public static void main(String[] args) {
		SubA obj = new SubA();
		obj.display();
	}
}

 

 

 


제어자 final



package oop.inheritance;
import java.security.PublicKey;

class AA {
	static final int MY_VAL = 10000;  //멤버변수에 final이 추가되면 상수. 상수는 대문자로 쓰기
	public final void display() { //메소드 앞에 final이 추가되면 오버라이딩이 불가능한 메소드가 됨
		System.out.println("AA");
		//myval = 1000;
	}
}

final class BB extends AA {
	public void display() {  // 오류 -> 조상 클래스의 선언부에서 final이 있기 때문에
                          	 // 부모가 갖고 있는 display를 재정의(오버라이딩)
		System.out.println("BB");
		
	}
}


public class FinalTest {

	public static void main(String[] args) {
		BB obj = new BB();
		obj.display();
	}
    
}

 

 

<예제>

package oop.polymorphism;

public class TestShape {
	public static void main(String[] args) {
		Shape [] shape = new Shape[2];
		shape[ 0 ]  = new Circle("원",10);
		shape[ 1 ] = new Rectangular("직사각형",10,20);
		
		for (int i = 0; i < shape.length; i++) {
			shape[ i ].calculationArea();
			shape[ i ].print();
		}
	}
}

위의 조건을 구현하기

 

// 조상 클래스
public abstract class Shape {
	private double area; //면적
	private String name; //이름
	public Shape(){
	}
	public Shape(String name) {
		super();
		this.name = name;
	}
	
	public abstract void calculationArea();
	
	public void print() {
		System.out.println(name+"의 면적은 "+area);
	}
	
	public double getArea() {
		return area;
	}
	public void setArea(double area) {
		this.area = area;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}
// 자손 클래스
public class Circle extends Shape {
	private double radius; //반지름
	public Circle() {
		
	}
	public Circle(String name, double radius) {
		super(name);
		this.radius = radius;
	}
	
	public  void calculationArea() {
		setArea(Math.PI*radius*radius);
	}
	
	public double getRadius() {
		return radius;
	}
	public void setRadius(double radius) {
		this.radius = radius;
	}
}
// 자손 클래스
public class Rectangular extends Shape {
	private double width;
	private double height;
	public Rectangular() {
		
	}
	public Rectangular(String name, double width, double height) {
		super(name);
		this.width = width;
		this.height = height;
	}
	
	public  void calculationArea() {
		setArea(height*width);
	}
	
	public double getWidth() {
		return width;
	}
	public void setWidth(double width) {
		this.width = width;
	}
	public double getHeight() {
		return height;
	}
	public void setHeight(double height) {
		this.height = height;
	}
}

* 추상클래스 이용하기

댓글