[day11] java-basic : 추상클래스

4 분 소요

추상메소드

  • ‘추상메소드’란 반드시 사용해야 하는 표준화 작업이 필요할 때 강제성을 부여하는 메소드이다.

  • A라는 클래스를 상속받은 B라는 클래스에는 C라는 메소드가 반드시 필요하다 라고 강제성을 부여하고 싶을 때, 내용부가 없이 {}만 있는 메소드 대신 추상 메소드를 사용한다.

    • A : 추상클래스
    • C : 추상메소드
  • 그래서 추상 클래스를 상속받은 하위 클래스는 상위 추상클래스의 추상메소드를 반드시 오버라이딩해야 한다.

  • ‘추상메소드’는 내용부({ })를 가지고 있지 않은 메소드이다.

  • 추상메소드를 작성할 때에는 키워드 abstract를 앞에 붙여주고, 구현부가 없으므로 괄호{ } 대신 문장 끝을 알리는 ;를 적어준다.

// 추상메소드
abstract void 메소드명();
  • 추상메소드를 포함하는 클래스는 반드시 추상클래스로 선언되어야 한다.

추상클래스

  • ‘추상클래스’란 1개 이상의 ‘추상메소드’를 가진 클래스이다.

  • 상속보다 더 변화에 둔감하게 만들기 위해 상속에 부가적으로 사용되는 기능이 바로 ‘추상클래스’이다.

  • 추상클래스는 덜 만들어진 설계도이므로, 추상클래스를 상속받은 자식 클래스로만 인스턴스 객체를 만들 수 있다.

  • 즉, 추상클래스만으로는 new키워드를 사용할 수 없다.

  • 추상클래스는 일반 구현된 메소드와 추상메소드를 모두 가질 수 있지만, 인터페이스는 추상메소드들의 집합이다.

  • 추상클래스 참조변수의 객체는 하위클래스를 이용한다.

abstract class Printer { }
class LGPrinter extends Printer { }
=====================================
class Main {
    public staitc void main(String[] args) {
        Printer p = new LGPrinter();
    }
}
  • ‘추상클래스’앞에는 abstract라는 키워드를 붙이기만 하면 된다.
// 추상클래스
abstract class Aniamal{

  // 추상메소드
  abstract void print();

}
  • 추상 클래스도 생성자가 있어야 한다.

실습해보기

추상클래스 없는 방식 : package kr.ac.kopo.day11

  • 클래스 만들때 상속받을 클래스 미리 설정해주기 : SuperClass 설정 image

  • 삼성이나 엘지나 다 목적은 출력인데 출력 메소드를 다 다르게 가지고 있어서(lgPrint(), samPrint()) 프린트 회사를 바꾸면 메소드명을 다 바꿔줘야 해서 좋지 않다. image

추상클래스 : package kr.ac.kopo.day11.abs02

  • 추상클래스와 추상메소드를 만들어서 상속받아 사용하기 때문에 새로운 프린터 회사가 추가되어도 쉽게 구현할 수 있다. image

  • 상속의 경우 상위클래스의 타입을 통해 하위 클래스의 인스턴스 객체를 생성할 수 있다.

public void print(int no) {

  // 상속의 경우 상위클래스의 타입을 통해 하위 클래스의 인스턴스 객체를 생성할 수 있다. (객체형변환)
  Printer p = null;

  switch (no) {
  case 1: 
    p = new LGPrinter();
    break;
  case 2: 
    p = new SamsungPrinter();
    break;
  case 3: 
    p = new HPPrinter();
    break;
  }

  // 세 가지 case 모두 같은 변수명과 메소드를 구현하기 때문에 한 번만 작성해줌으로써 코드를 줄일 수 있다.
  if(p != null)
    p.print();
  else {
    System.out.println("잘못 선택하셨습니다.");
  }

}

HP가 잉크젯과 레이저 사업부로 분사된 경우 : package kr.ac.kopo.day11.abs03

image

객체형변환

  • 다형성의 기본 원리가 바로 객체 형변환이다.

  • 대입 연산자 = 를 기준으로 좌변과 우변 항의 형이 다른 경우 형변환이 일어난다

  • 객체 형변환은 오직 대입 연산자에서만 일어난다.

  • 객체 형변환이 발생하려면 좌변과 우변의 관계가 서로 상속관계를 가지고 있어야 한다.

abstract class Printer { }
class LGPrinter extends Printer { }
=====================================
class Main {
    public staitc void main(String[] args) {
        Printer p = new LGPrinter();
    }
}
  • 객체 형변환의 종류 : 1) 묵시적 형변환, 2) 명시적 형변환

묵시적 형변환

  • 부모 클래스 타입 = 자식 클래스 타입

  • 자식클래스의 인스턴스 객체 주소를 부모클래스 타입의 참조변수가 가리키게 되는 것이다.

abstract class Printer { }
class LGPrinter extends Printer { }
=====================================
class Main {
    public staitc void main(String[] args) {
        Printer p = new LGPrinter();
    }
}
  • 만약 위의 예시에서 LGPrinter는 Printer객체와 LGPrinter 객체를 둘 다 가지고 있기 때문에, Printer p = new LGPrinter()에서 부모의 참조변수인 Printer p가 접근할 수 있는 범위는 Printer객체 뿐이다.

  • 단, 상위클래스의 메소드를 하위클래스에서 오버라이딩했을 경우 Printer p가 하위클래스에 오버라이딩된 메소드를 호출할 수 있다.

  • Printer p = (Printer) new LGPrinter()로 작성해야 하지만, 묵시적 형변환에 의해 (Printer)를 생략할 수 있다. image

  • List list = new ArrayList();도 묵시적 형변환이 자주 사용되는 예시이다. 만약 자료구조를 LinkedList로 바꾸고 싶다면, List list = new LinkedList();로만 바꿔주면 된다.

명시적 형변환

  • 자식클래스 타입 = (자식 클래스 타입) 부모 클래스 타입
abstract class Printer { }
class LGPrinter extends Printer { }
===============================================
class Main {
    public staitc void main(String[] args) {
        Printer p = new LGPrinter();
    }
}
  • 이때, LGPrinter p = new Printer();이나 LGPrinter p = (LGPrinter) new Printer(); 은 모두 컴파일 에러이다. LGPrinter의 인스턴스 객체가 만들어지지 않은 상태이기 때문이다.

  • Printer p = new Printer();이나 LGPrinter lgtv = (LGPrinter) p;는 컴파일 에러는 아니고 실행시 오류가 발생한다.

  • 객체를 생성하지도 않고 형변환을 시키려고 하면 에러가 발생할 수 밖에 없다. 그래서 무조건 형변환할 타입의 객체를 만들어줘야 한다.

  • 명시적 형변환을 통해 묵시적 형변환에서 실행시킬 수 없는 자식클래스만의 메소드를 실행시킬 수 있다.

// 묵시적 형변환
Parent p = new Child02();
p.info();

// 만약 Child02만 가지고 있는 메소드를 실행시키고 싶을 때, 명시적 형변환을 한다.
Child02 c = (Child02) p;
c.sing();
c.play();

  • 대입연산자 오른쪽에 나와야 하는 상위 type의 변수는 이미 묵시적 형변환이 되어 있는 객체여야 한다.
Printer p = new LGPrinter();  // 묵시적 형변환
LGPrinter lg = (LGPrinter) p; // 명시적 형변환
  • instanceof 연산자
public class CastMain03 {
	
	public static void print(Parent p) {
		p.info();
		
		System.out.println("[p instanceof Child01] : " + (p instanceof Child01));
		System.out.println("[p instanceof Child02] : " + (p instanceof Child02));
		System.out.println("[p instanceof Parent] : " + (p instanceof Parent));
		
		if(p instanceof Child01) {
			
			Child01 c = (Child01) p;
			c.sleep();
			c.study();
			
		}else if (p instanceof Child02) {
			
			Child02 c = (Child02) p;
			c.sing();
			c.play();
			
		}
	}	
	
	public static void main(String[] args) {
		
		Parent p = new Child02();
		print(p);
		
		System.out.println("--------------------------------------------------------------");
		
		Child01 c01 = new Child01();
		print(c01);
		
		System.out.println("--------------------------------------------------------------");

		// 만약 Child02만 가지고 있는 메소드를 실행시키고 싶을 때, 명시적 형변환을 한다.
		Child02 c = (Child02) p;
		print(c);
		
	}
}