고래씌

[JAVA] 10-3. 다형성(추상클래스 abstract 와 인터페이스) 본문

JAVA/JAVA 기초

[JAVA] 10-3. 다형성(추상클래스 abstract 와 인터페이스)

고래씌 2023. 10. 18. 09:54

1. 추상클래스

- 미완성된 클래스 abstract class
- 객체생성 불가능(단, 참조변수(레퍼런스 변수)로는 선언 가능)
- 추상클래스 내부에 추상메소드가 존재하는 순간 해당 클래스는 반드시 추상클래스로 정의
   (일반필드 + 일반 메소드 + [추상메소드(생략가능)])
 
 
단, 추상메소드가 굳이 없어도 추상클래스로 둘 수 있다.
언제? ① 클래스가 아직 구체적으로 덜 구현된 상태인 것으로 보이는 경우(개념적)
          ② 현재 이 클래스를 객체 생성이 불가하게끔 하고자 할 때(기술적)
 
=> 객체 생성은 불가능한데 참조변수로 선언 가능하다라는 특징다형성을 적용할 수 있다로 해석이 됨.
 
 
- 몸통부가 존재하지 않는 미완성 메소드 == 추상 메소드
- 몸통부가 없는 미완성 메소드를 정의하고자 하면 abstract 예약어를 사용해야한다.
=> 미완성 메소드가 하나라도 현재 클래스에 포함되는 순간, 해당 클래스 또한 미완성 클래스가 되어버린다.
따라서, 클래스명 앞에도 abstract 예약어를 적어줘야한다.
 

추상클래스 Sports 클래스
자식클래스 Basketball 클래스
자식클래스 Football 클래스

 
=> 미완성된 클래스인 추상클래스를 상속받게되면, 부모클래스에 있는
미완성된 메소드(추상메소드)를 강제로 오버라이딩 해줘서 완성시켜야함
 

main 메소드가 포함된 Run 클래스

 
=> 추상클래스는 객체 생성이 불가능하다! 미완성 클래스이기 때문.
=> 단, 객체생성은 불가능하지만, 참조변수로는 사용 가능! 
=> 다형성을 적용해서 자식객체를 생성하고 이를 저장하는 용도로 사용이 가능하다!
 

main 메소드가 포함된 Run 클래스

2. 추상메소드

- 미완성된 메소드몸통부{}가 구현되어 있지 않은 메소드
- 자식클래스에서 오버라이딩을 통해 완성됨(강제로 오버라이딩 해야함)
  => 오버라이딩 하지 않을 경우 에러가 발생
  => 메소드 사용의 통일성 확보 목적
  => 표준화된 틀을 제공할 수 있는 목적
 

3. 인터페이스

- 인터페이스
[표현법]
접근제한자 interface 인터페이스명 {}
 
- 상수필드추상메소드만 이루어진 추상 클래스의 변형체다!
- 메소드 통일성을 부여하기 위해 추상 메소드만 따로 모아놓은 것으로 상속시 인터페이스 내에 정의된 모든 추상메소드 구현해야함
- 인터페이스에서 선언한 필드는 명시하지 않아도 무조건 상수필드로 사용
- 아래와 같이 "stactic final" 을 붙이지 않아도 무조건 상수로 사용되는 것을 확인
 

 
- 인터페이스에서 선언한 메소드는 명시하지 않아도 무조건 추상메소드이다.
- 인터페이스에서는 추상메소드만 정의할 수 있기 때문에 메소드의 몸통부 작성이 불가능하며, abstract 키워드가 자동으로 추가됨.
ex)
pulibc void eat() {        => 에러 발생
}
 
void eat();   => O

 
- 무조건 구현해야하는 메소드가 존재할 경우 인터페이스를 만들어서 상속하게 한다.
- 인터페이스는 ★다중 상속을 허용한다. 참고)일반 클래스는 다중 상속 X
=> 결국 메소드에 정의만 하고 있는게 인터페이스이기 때문에, 여러 메소드가 겹치더라도 최종 구현은 구현클래스(자식클래스)에서 이루어질 것이기 때문에 다중 상속(구현)을 받아도 문제가 없다.   (인터페이스에서는 상속을 구현이라고 함)
 
 
- 추상 클래스와 다르게 강한 규칙성과 강제성을 가짐
 
 
(1) ★공통점
①  객체 생성은 안되나, 참조변수로서 사용이 가능함.(즉, 다형성을 적용할 수 있다)
②  상속(구현)하는  클래스에 추상메소드를 구현하도록 강제한다.
 
 
(2) ★차이점
추상클래스는 클래스 내에 인스턴스 변수, 메소드를 생성이 가능하고, 추상메소드가 포함되어있거나 abstract 키워드로 클래스가 정의되어 있고
인터페이스는 인스턴스 변수, 일반 메소드 생성이 불가능하며 모든 변수는 상수필드, 모든 메소드는 추상메소드로 정의되어 있다.
 
③ ★★★존재하는 목적이 다르다★★★
    - 추상클래스는 추상클래스를 상속받아서 기능을 이용하고, 클래스를 "상속"하는데 목적이 있음
    - 인터페이스는 클래스의 기능(함수)구현을 강제하기 위해 사용됨. 즉, 기능구현을 강제함으로써 인터페이스를 상속받은 모든 클래스에 동일한 동작을 보장할 수 있음
 
 
(3) extends와 implements
- 클래스 간에 상속 관계일 때 : 클래스명 extends 부모클래스명
- 클래스와 인터페이스의 구현 관계일 때 : 클래스명 implements 인터페이스명, 인터페이스명 ...
- 인터페이스간의 상속 관계일 때 : 인터페이스명 extends 인터페이스명, 인터페이스명
 
 
※ 정리
▶ 클래스에서 클래스를 상속 받을 때 : extends 클래스(단일상속만 가능) : 화살표 실선
▶ 클래스에서 인터페이스를 구현할 때 : implements 인터페이스, 인터페이스...(다중구현 가능) : 화살표 점선
▶ 인터페이스에서 인터페이스를 상속 : extends 인터페이스, 인터페이스..(다중상속 가능) : 화살표 실선
 

 추상클래스인터페이스
상속개수단일상속다중상속
키워드extendsimplements
추상메소드
표현법/개수
추상메소드 0개 이상
명시적 abstract 기술
모든 메소드가 추상메소드
묵시적으로 abstract 기술
일반메소드OX
필드 일반필드 가질 수 있음 상수필드만 가질 수 있음(묵시적 static final)

 
<배운순서>
extends 일반클래스 → extends 추상클래스 → implements 인터페이스
---------------------------------------------------------------------------------------------->
          뒤로 갈수록 기능 구현의 강제성이 더 짙어진다.
 
 

1) 인터페이스 적용 전

<추상클래스 Person>

package com.kh.chap02_abstractAndInterface.part02_family.model.vo;

public abstract class Person {

	private String name;
	private double weight;
	private int health;
	
	public Person() {
		
	}

	public Person(String name, double weight, int health) {
		super();
		this.name = name;
		this.weight = weight;
		this.health = health;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public double getWeight() {
		return weight;
	}

	public void setWeight(double weight) {
		this.weight = weight;
	}

	public int getHealth() {
		return health;
	}

	public void setHealth(int health) {
		this.health = health;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", weight=" + weight + ", health=" + health + "]";
	}
	
	public abstract void eat();
	public abstract void sleep();
}

<자식클래스 Mother 클래스>

package com.kh.chap02_abstractAndInterface.part02_family.model.vo;

public class Mother extends Person{
	
	private String babyBirth; // 아기의 탄생 여부 : 출산, 입양, 없음
	
	public Mother() {
		
	}
	
	public Mother(String name, double weight, int health, String babyBirth) {
		super(name, weight, health);
		this.babyBirth = babyBirth;
	}

	public String getBabyBirth() {
		return babyBirth;
	}

	public void setBabyBirth(String babyBirth) {
		this.babyBirth = babyBirth;
	}

	@Override
	public String toString() {
		return "Mother [babyBirth=" + babyBirth + ", toString()=" + super.toString() + "]";
	}

	@Override
	public void eat() {
		// 엄마가 밥을 먹으면?
		// 몸무게 10 증가
		super.setWeight(super.getWeight()+10);  // 수정할 몸무게는 기존의 몸무게 + 10
		
		// 건강도 10 감소
		super.setHealth(super.getHealth()-10);
	}

	@Override
	public void sleep() {
		// 건강도 10 증가
		super.setHealth(super.getHealth()+10);
		
	}
}

<자식클래스 Baby 클래스>

package com.kh.chap02_abstractAndInterface.part02_family.model.vo;

public class Baby extends Person{

	public Baby() {
		
	}
	
	public Baby(String name, double weight, int health) {
		super(name, weight, health);
	}
	
	@Override
	public String toString() {
		return "Baby [toString()=" + super.toString() + "]";
	}

	// eat -> 몸무게 3증가, 건강도 1증가
	@Override
	public void eat() {
		super.setWeight(super.getWeight()+3);	
		super.setHealth(super.getHealth()+1);
	}

	// slepp -> 건강도 3증가
	@Override
	public void sleep() {
		super.setHealth(super.getHealth()+3);
	}

}

<main 메소드가 포함된 Run 클래스>

package com.kh.chap02_abstractAndInterface.part02_family.run;

import com.kh.chap02_abstractAndInterface.part02_family.model.vo.Baby;
import com.kh.chap02_abstractAndInterface.part02_family.model.vo.Mother;
import com.kh.chap02_abstractAndInterface.part02_family.model.vo.Person;

public class Run {

	public static void main(String[] args) {
		// 인터페이스 적용 전
		
//		Person p = new Person(); // 에러 발생. 미완성 클래스니까(추상클래스)
		
		Person p1 = new Mother("범고래", 50, 50, "출산");   // 상속구조에서만 업캐스팅이 발생한다
		Person p2 = new Baby("이고래", 3.5, 60);
		
		System.out.println(p1);
		System.out.println(p2);
		
		p1.eat();  // Mother 60 40
		p1.sleep(); // Mother 60 50
		
		p2.sleep(); // Baby 6.5 61
		p2.eat(); // 6.5 64
		
		System.out.println("== 다음날 ==");

		System.out.println(p1);
		System.out.println(p2);

	}

}

=> 추상클래스는 자식클래스를 상속하는데 목적!
 

2) 인터페이스 적용 후

인터페이스 Basic 클래스
자식클래스 Mother 클래스
자식클래스 Baby 클래스