몸체 없는 메소드를 포함한 클래스 (미완성 설계도)
추상 클래스일 경우 클래스 선언부에 abstract 키워드 사용
[접근제한자] abstract class 클래스명 {}
몸체 없는 메소드
추상 메소드의 선언부에 abstract 키워드 사용
상속 시 반드시 구현해야 하는, 오버라이딩이 강제화되는 메소드
[접근제한자] abstract 반환형 메소드명(자료형 변수명);
미완성 클래스(abstract 키워드 사용)
자체적으로 객체 생성 불가 → 반드시 상속하여 객체 생성
abstract 메소드가 포함된 클래스는 반드시 abstract 클래스
abstract 메소드가 없어도 abstract 클래스 선언 가능
클래스 내에 일반 변수, 메소드 포함 가능
객체 생성은 안되지만 참조형 변수 타입으로는 사용 가능
상속 받은 자식에게 공통된 멤버 제공
일부 기능의 구현을 강제화(공통적이나 자식 클래스에 따라 재정의 되어야 하는 기능)
❗ 연관된 클래스 ❗의 공통점을 묶어두되 부모 클래스는 객체로 만들 수 없는 경우 + 일부 미완성 클래스(abstract 메소드 0개 이상 포함)
→ abstract 메소드가 없어도 클래스를 객체로 못 만들게 하고 싶은 경우
ex) 계산기 → 기본 기능을 가지고 있지만 이것만 가지곤 쓸모가 없음
→ abstract로 만들어서 객체 생성 막아둠
또는 의미적으로 객체가 되면 안 되는 경우
ex) Biology - 생물 객체는 어떤 생물인가?
ex) Animal 클래스
동물 객체는 어떤 동물인가? eat(), breath()는 어떻게 수행되는가?
→ 정의 내릴 수 없다, 알 수 없다. 하지만 동물의 공통된 기능 명은 알고 있음

다형성을 이용하여 상위 타입 역할(자식 객체 연결)
인터페이스 구현 객체에 공통된 기능 구현 강제화 (== 구현 객체간의 일관성 제공)
공동 작업을 위한 인터페이스 제공

인터페이스 : 접점
UI(User Interface) : 사용자와의 접점 = 화면
인터페이스 : 클래스간의 접점 - 클래스가 공통적으로 가져야되는 필드, 메소드를 모아두고 상속
❗ 연관성 낮거나 없는 ❗ 클래스들에게 공통된 기능을 제공할 때 사용
ex) 키보드, 마우스, 스캐너, 카메라, 압력 센서
→ 입력 장치(각각의 용도는 다르지만 "입력"이라는 공통된 "기능명"이 있을 뿐
입력이라는 접점만 있는거지 실질적 기능은 각각 다르기 때문에, 전부 다 추상 메소드 + 모든 필드가 묵시적(암묵적, 자동적) public static final
ex) public static final double PI = 3.141592;
(public static final) int MAX_NUM = 10000;
+ 모든 메소드가 묵시적으로 public abstract (추상 메소드)
ex) ( public abstract ) void input();
// "input"이라는 이름을 자식에게 제공할 뿐
// 상세한 기능은 자식이 알아서 오버라이딩 해라
// -> 추상 메소드이기 때문에 오버라이딩 강제화!
(참고) 인터페이스 활용 용도
→ default 메소드 : 상속 받은 클래스에 공통 기능 제공
→ 자바의 함수 지향 언어 사용을 위한 메소드로 사용
/+ 람다, 스트림에 사용 + Spring AOP Proxy 사용
abstract class Animal
package edu.kh.poly.ex2.model.vo;
public abstract class Animal {
// 추상 클래스(abstract class)
// 1. 미완성 메소드(추상 메소드)를 보유하고 있는 클래스
// 2. (추상 클래스인데 추상 메소드 없을수도 있음)객체로 만들면 안 되는 클래스
// 필드
private String type; // 종/과 구분
private String eatType; // 식성(초식, 육식, 잡식)
// 생성자
// - 추상 클래스는 new 연산자를 이용해서 직접적인 객체 생성은 불가능하지만
// 상속 받은 객체 생성 시 내부에 생성될 때 사용된다.
// ==super() 생성자
public Animal() { // 기본 생성자
super(); // 생략 시 컴파일러가 추가
}
public Animal(String type, String eatType) { // 매개변수 생성자(오버로딩 적용)
this.type = type;
this.eatType = eatType;
}
// 메소드
// getter / setter
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getEatType() {
return eatType;
}
public void setEatType(String eatType) {
this.eatType = eatType;
}
// toString() 오버라이딩
@Override // 오버라이딩 되었음을 컴파일러에게 알려주는 어노테이션
public String toString() {
return type + " / " + eatType;
}
// 동물의 공통 기능 추출(추상화)
// -> 동물은 공통적으로 먹고, 숨쉬지만
// 어떤 동물이냐에 따라 그 방법이 다름! -> 해당 클래스에 메소드를 정의할 수 없다
// --> 해결방법 : 미완성 상태로 두어 상속 받은 자식이
// 해당 메소드를 정의 하도록 오버라이딩을 강제화 시킴
// 추상(abstract) 메소드로 작성
// 먹다
public abstract void eat();
// 숨쉬다
public abstract void breath();
}
class Fish extends Animal
package edu.kh.poly.ex2.model.vo;
public class Fish extends Animal{
// 생성자
// -> 생성자 미 작성시 컴파일러가 자동으로 기본 생성자를 추가해줌
public Fish() {
super();
}
public Fish(String type, String eatType) {
super(type, eatType);
}
// 추상 메소드는 상속 받으면 오버라이딩이 강제된다
@Override
public void eat() {
System.out.println("입을 뻐끔 뻐끔 거리면서 먹는다");
}
@Override
public void breath() {
System.out.println("아가미로 호흡한다");
}
@Override
public String toString() {
return "Fish : " + super.toString();
}
}
class Person extends Animal
package edu.kh.poly.ex2.model.vo;
public class Person extends Animal{
// Animal의 추상 메소드를 오버라이딩 하지 않으면 오류 발생
private String name;
// 생성자
public Person() {
super(); // == Animal 기본 생성자
}
public Person(String type, String eatType, String name) {
super(type, eatType);
this.name = name;
}
// getter / setter
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void eat() {
System.out.println("숟가락, 젓가락, 나이프, 포크 등을 이용해서 먹는다");
}
@Override
public void breath() {
System.out.println("코와 입으로 숨을 쉰다");
}
// toString()
public String toString() {
return "Person : " + super.toString() + " / " + name;
}
}
class AbstractRun
package edu.kh.poly.ex2.run;
import edu.kh.poly.ex2.model.service.AbstractService;
import edu.kh.poly.ex2.model.service.Calculator;
import edu.kh.poly.ex2.model.service.KSYCalculator;
public class AbstractRun {
public static void main(String[] args) {
AbstractService service = new AbstractService();
// service.ex1();
// service.ex2();
// Calculator 임폴트
// import edu.kh.poly.ex2.model.service.Calculator;
// 각자 만든 xxxCalculator 임폴트
// import edu.kh.poly.ex2.model.service.OYJCalculator;
// Calculator cal = new OYJCalculator();
Calculator cal = new KSYCalculator();
// 서로 관련 없는 클래스가 Calculator로 접점이 생겨서 동일하게 수행되는 코드가 수행된다
// Calculator cal = new Calculator();
// 인터페이스 == 미완성 설계도 == 객체 생성 불가능
// 코드의 큰 수정 없이
// 객체 생성 코드만 바꾸면 새로운 클래스 코드를 수행할 수 있다
// 유지보수성에 좋다
System.out.println("합 : " + cal.plus(20, 15));
System.out.println("차 : " + cal.minus(20, 15));
System.out.println("곱 : " + cal.multiple(20, 15));
System.out.println("몫 : " + cal.divide(20, 15));
// 인터페이스의 특징
// 1) 인터페이스를 부모 참조 변수로 사용하면 다형성 업캐스팅이 적용되어서
// 상속 받은 모든 클래스를 자식 객체로 참조할 수 있다
// -> 이를 이용하여 중요한 메인 코드의 수정을 최소화 할 수 있다
// ex) Run에 작성된 합, 차, 곱, 몫을 출력하는 코드의 수정 없이
// 객체 생성 코드 한 줄만 수정하여 다르게 작성된 기능을 수행할 수 있다
// 2) 자식 클래스에 공통된 메소드 구현을 강제하기 때문에
// 모든 자식 클래스가 동일한 형태를 띄게 된다.
// -> 이를 이용하여 공동 작업(팀 프로젝트)에 필요한
// 개발 환경을 조성할 수 있다.
}
}
class AbstractService
package edu.kh.poly.ex2.model.service;
import edu.kh.poly.ex2.model.vo.Animal;
import edu.kh.poly.ex2.model.vo.Fish;
import edu.kh.poly.ex2.model.vo.Person;
public class AbstractService {
public void ex1() {
// 추상 클래스는 객체로 만들 수 있을까?
// Animal a1 = new Animal();
// 오류 발생 : Cannot instantiate the type Animal(객체화 할 수 없다)
// 클래스 : 객체의 속성, 기능을 정의한 것(일종의 설계도)
// 추상 클래스 : 미완성 메소드를 포함한 클래스(미완성 설계도)
// -> 미완성 설계도로는 객체를 만들 수 없다! -> 오류 발생
// 해결 방법 : Animal을 상속 받아 미완성 부분을 구현한 클래스(자식)를 이용해 객체 생성
// * 추상 클래스를 상속 받은 자식 객체 생성하기
Person p1 = new Person();
p1.setName("홍길동");
// 상속 받은 기능 호출
p1.setType("척추동물");
p1.setEatType("잡식");
// 오버라이딩한 메소드 호출
p1.eat();
p1.breath();
Fish f1 = new Fish();
f1.breath();
}
public void ex2() {
// * 추상 클래스와 다형성 + 바인딩
// - 추상 클래스는 객체로 만들 수 없다!!!
// --> 하지만 "참조 변수"로는 사용할 수 있다!!!
// ex) 동물은 사람인가? 물고기인가? 알 수 없다
// Animal a1 = new Animal(); (x)
// 사람은 동물이다?, 물고기는 동물이다? 맞다
// Animal a1 = new Person();
// Animal a2 = new Fish();
// Animal 참조 변수 배열 선언 및 할당
Animal[] arr = new Animal[2];
arr[0] = new Person("사람", "잡식", "김사랑");
// Animal 부모 = Person 자식(다형성 중 업캐스팅)
arr[1] = new Fish("물고기", "잡식");
// Animal 부모 = Fish 자식(다형성 중 업캐스팅)
// 바인딩 확인
for(int i=0; i<arr.length; i++) {
arr[i].eat();
arr[i].breath();
System.out.println(arr[i]);
// print 메소드에 참조변수 작성 시 자동으로 toString() 호출
// arr의 i번째 인덱스 arr[i]==Animal 참조변수
// void edu.kh.poly.ex2.model.vo.Animal.eat() - 정적 바인딩
// 프로그램 실행 시
// 참조하고 있는 자식 객체의 오버라이딩 된 eat() 메소드 수행 - 동적 바인딩
// (부모 타입 참조 변수로 메소드를 호출 했지만 자식 타입에 오버라이딩된 메소드가 수행된다)
// 업캐스팅 상태(부모 참조 = 자식 객체)에서
// 부모 메소드 호출 시, 오버라이딩된 자식 메소드 수행
}
}
}

class Calculator
package edu.kh.poly.ex2.model.service;
// 계산기 인터페이스
// -> 모든 계산기에 대한 공통 필드, 기능명을 제공하는 접점(interface)용도
public interface Calculator {
// 인터페이스 : 추상클래스의 변형체(추상 클래스 보다 추상도가 높음)
// - 추상 클래스 : 일부만 추상 메소드(0개 이상), 즉 없을 수도 있음
// - 인터페이스 : 모두 추상 메소드, 즉 추상 클래스보다 더 추상이 많기에 추상도가 높다고 표현
// 부모로부터 만들어진 게 없고 모두 다 추상 메소드이기에 extends 사용이 아닌 implements를 사용한다
// 필드(묵시적으로 public static final)
// -> 어떻게 작성하든 모든 public static final
public static final double PI = 3.14;
static final int MAX_NUM = 10000;
public int MIN_NUM = -10000;
int ZERO = 0;
// 메소드(묵시적으로 public abstract)
public abstract int plus(int num1, int num2); // 추상메소드
public abstract int minus(int num1, int num2);
public abstract int multiple(int num1, int num2);
public abstract int divide(int num1, int num2);
}
class KSYCalculator
package edu.kh.poly.ex2.model.service;
public class KSYCalculator implements Calculator{
// extends : 확장하다, implements : 구현하다
// (부)클래스 - (자)클래스 상속시에는 extends (추상 클래스도 포함)
// (부) 인터페이스 - (자)클래스 상속 시 implements
@Override
public int plus(int num1, int num2) {
return num1+num2 + MAX_NUM;
}
@Override
public int minus(int num1, int num2) {
return num1-num2 + MAX_NUM;
}
@Override
public int multiple(int num1, int num2) {
return num1*num2 + MAX_NUM;
}
@Override
public int divide(int num1, int num2) {
return num1/num2 + MAX_NUM;
}
}
class OYJCalculator
package edu.kh.poly.ex2.model.service;
public class OYJCalculator implements Calculator{
// 클래스, 인터페이스는 동시 상속 가능
// 인터페이스는 다중 상속 가능
// extends : 확장하다, implements : 구현하다
// (부모)클래스 - (자식)클래스 상속 시에는 extends (추상 클래스도 포함)
// (부모)인터페이스 - (자식)클래스 상속 시에는 implements
@Override
public int plus(int num1, int num2) {
return num1 + num2;
}
@Override
public int minus(int num1, int num2) {
return num1 - num2;
}
@Override
public int multiple(int num1, int num2) {
return num1 * num2;
}
@Override
public int divide(int num1, int num2) {
return num1 / num2;
}
}
