객체 지향 언어의 특징 중 하나로 '다양한 형태를 갖는다'라는 뜻으로 하나의 행동으로 여러 가지 일을 수행하는 개념.
상속을 이용한 기술로 부모 클래스 타입 참조변수 하나로 상속 관계에 있는 여러 타입의 자식 객체를 참조할 수 있는 기술
부모클래스 변수명 = new 자식클래스();
둘이 자료형이 다름
원래 같은 자료형끼리만 연산 가능하다는 연산 규칙때문에 다른 자료형끼리 연산 불가
이걸 가능하게끔 해주는 게 상속
부모 class Shape에
Shape shape = new Circle();
Shape shape = new Triangle();

클래스 자료형을 형변환하는 거
상속 관계에 있는 부모, 자식 클래스 간에 부모타입의 참조형 변수가 모든 자식 타입의 객체의 주소를 참조할 수 있음
// Sonata 클래스는 Car 클래스의 후손
Car c = new Sonata();
// 자식인데 부모인 Car 형태도 된다 == Up Casting
// 자식이 부모로 형태를 변환
// Sonata 클래스형에서 Car 클래스형으로 바뀜
Car 클래스
package edu.kh.poly.ex1.model.vo;
public class Car {
// 속성
private String engine; // 엔진
private String fuel; // 연료
private int wheel; // 바퀴 개수
public Car() {} // 기본 생성자
// alt shift s o enter 단축키
public Car(String engine, String fuel, int wheel) {
super();
this.engine = engine;
this.fuel = fuel;
this.wheel = wheel;
}
// 단축키 alt shift s r
public String getEngine() {
return engine;
}
public void setEngine(String engine) {
this.engine = engine;
}
public String getFuel() {
return fuel;
}
public void setFuel(String fuel) {
this.fuel = fuel;
}
public int getWheel() {
return wheel;
}
public void setWheel(int wheel) {
this.wheel = wheel;
}
// 단축키 alt shift s toString
// Object.toString() 오버라이딩
// 오버라이딩 했다는 걸 컴파일러에게 알려주는 Override 어노테이션 (컴파일러 인식용 주석)
@Override
public String toString() {
return engine + " / " + fuel + " / " + wheel;
}
}
Spark 클래스
package edu.kh.poly.ex1.model.vo;
public class Spark extends Car { // 경차
private double discountOffer; // 할인혜택
public Spark() {}
public Spark(String engine, String fuel, int wheel, double discountOffer) {
super(engine, fuel, wheel);
this.discountOffer = discountOffer;
}
public double getDiscountOffer() {
return discountOffer;
}
public void setDiscountOffer(double discountOffer) {
this.discountOffer = discountOffer;
}
@Override
public String toString() {
return super.toString() + " / " + discountOffer;
}
}
Tesla 클래스
package edu.kh.poly.ex1.model.vo;
public class Tesla extends Car { // 전기차
private int batteryCapacity; // 배터리 용량
public Tesla() {}
public Tesla(String engine, String fuel, int wheel, int batteryCapacity) {
super(engine, fuel, wheel);
this.batteryCapacity = batteryCapacity;
}
public int getBatteryCapacity() {
return batteryCapacity;
}
public void setBatteryCapacity(int batteryCapacity) {
this.batteryCapacity = batteryCapacity;
}
// Car.toString() 오버라이딩
@Override
public String toString() {
return super.toString() + " / " + batteryCapacity;
}
}
Service 클래스
다형성 확인 예제
package edu.kh.poly.ex1.model.service;
import edu.kh.poly.ex1.model.vo.Car;
import edu.kh.poly.ex1.model.vo.Spark;
import edu.kh.poly.ex1.model.vo.Tesla;
public class PolyService {
public void ex1() {
// Car 객체 생성
Car car = new Car();
// 부모 타입 참조변수 = 부모객체
// Tesla 객체 생성
Tesla tesla = new Tesla();
// 자식 타입 참조변수 = 자식 객체
// 다형성
// 부모타입 참조변수 = 자식객체
Car car2 = new Tesla(); // 오류 발생 안함
// Tesla 객체를 참조하는 변수의 타입이 Car(부모)이기 때문에
// Tesla 객체가 Car(부모) 객체로 변화함.
Car car3 = new Spark();
다형성(업캐스팅) 작성 방법
자식 객체가 부모 객체로 변하였기 때문에 자식만의 고유한 필드, 메서드를 사용할 수 없다.
// 1) car (부모 = 부모)
car.setEngine("v6 6기통 엔진");
car.setFuel("휘발유");
car.setWheel(4);
// Car 메서드 모두 사용 가능
// 2) tesla (자식 = 자식)
tesla.setEngine("전기모터");
tesla.setFuel("전기");
tesla.setWheel(4);
tesla.setBatteryCapacity(1000000);
// Tesla 부모, 본인의 메서드 모두 사용 가능
// 3) car2 (부모 = 자식(Tesla))
car2.setEngine("전기모터");
car2.setFuel("전기");
car2.setWheel(4);
// car2.setBatteryCapacity(1000000);
// The method setBatteryCapacity(int) is undefined for the type Car
// Car 에 있는 것밖에 사용 못함
// 4) car3 (부모 = 자식(Spark))
car3.setEngine("경차 엔진");
car3.setFuel("휘발유");
car3.setWheel(4);
// car3.setDiscountOffer(0.5);
}
}
Spark, Tesla에서 작성된 고유 기능 사용 못함
객체 배열 : 같은 객체 참조 자료형의 변수를 하나의 묶음으로 다루는 것
부모 타입 참조 자료형의 변수를 하나의 묶음으로 다루는 것
상속 + 다형성
상속 특징 : Car 상속 클래스는 모두 getEngine()을 가지고 있다를 정의
다형성 (업 캐스팅) : 부모 타입 참조변수 arr[i]로 자식객체를 참조할 수 있다.
Car[] arr = new Car[3]; // 부모타입 참조변수 배열 선언 및 할당
// 각 배열 요소 Car 타입 참조변수
arr[0] = car; // Car 주소
arr[1] = car2; // Tesla 주소
arr[2] = car3; // Spark 주소
// 다형성 적용돼서 아무 문제 없음
for(int i = 0 ; i < arr.length ; i++) {
System.out.println(i + "번째 인덱스의 엔진 : " + arr[i].getEngine());
}
}
다형성을 이용한 반환형 사용법
Car[] arr = { new Car(), new Tesla(), new Spark() };
public void ex2() {
Tesla t = new Tesla("전기모터", "전기", 4, 1000000);
Spark s = new Spark("경차엔진", "휘발유", 4, 0.5);
Car c = new Car("경유엔진", "경유", 12);
printCar(t); // Tesla
printCar(s); // Spark
printCar(c); // Car
System.out.println("===============================================");
메서드 정의 1
public Car createCar(int num) {
Car result = null;
switch(num) {
case 1 : result = new Car(); break;
case 2 : result = new Tesla(); break;
case 3 : result = new Spark(); break;
}
return result;
}
메서드 정의 2
전달받은 Car 또는 자식객체(Tesla, Spark)의 엔진, 연료, 바퀴갯수를 출력하는 메서드
매개변수에 부모타입 참조변수를 작성하면 모든 자식 객체를 전달받을 수 있음
부모타입 참조변수 = 자식타입객체
다형성의 업캐스팅 모양과 똑같다
=> temp 에는 Tesla, Spark, Car의 주소가 넘어와도 된다(업캐스팅)
public void printCar(Car temp) {
System.out.println("엔진 : " + temp.getEngine());
System.out.println("연료 : " + temp.getFuel());
System.out.println("바퀴 개수 : " + temp.getWheel() + "개");
System.out.println(); // 줄바꿈
}
instanceof 연산자 : 객체의 자료형을 검사하는 연산자
-> 참조하는 객체가 특정 자료형이거나 부모쪽 상속관계인지 확인
Car[] arr = {createCar(1), createCar(2), createCar(3)};
//Car //Car(Tesla) //Car(Spark)
// 자식이라서 Car 자료형에서 어긋나지 않음
System.out.println(arr[1] instanceof Tesla); // true
System.out.println(arr[1] instanceof Spark); // false
System.out.println(arr[1] instanceof Car); // true
}
상속 관계에서 다형성 적용됨

Tesla 안에 있는 애들 사용하지 못함
자식 객체의 주소를 받은 부모 참조형 변수를 가지고 자식의 멤버를 참조해야 할 경우, 부모 클래스 타입의 참조형 변수를 자식 클래스 타입으로 형변환 하는 것
자동으로 처리되지 않기 때문에 반드시 자식 타입을 명시하여 형변환(강제 형변환)
//Sonata 클래스는 Car 클래스의 후손
Car c = new Sonata();
((Sonata)c).moveSonata();
다형성 중 다운캐스팅
다운캐스팅이란?
부모타입 참조변수가 자식객체를 참조하는 기술로 업캐스팅 상태에서만 진행할 수 있다.
부모타입을 자식타입으로 "강제 형변환"해서 자식 객체의 본래 필드, 메서드를 사용 가능하게 한다.
주의 사항
(연산자 우선순위)
"." 연산자가 (Tesla) 형변환 연산자보다 우선순위가 높음
형변환 먼저 해줘야함 ()로 한번 더 묶어줌
public void ex3() {
Car c1 = new Tesla("전기모터", "전기", 4, 50000);
// 업캐스팅 상태
System.out.println(((Tesla)c1).getBatteryCapacity());
Tesla t1 = (Tesla)c1;
System.out.println(t1.getBatteryCapacity());
}
현재 참조형 변수가 어떤 클래스 형의 객체 주소를 참조하고 있는지 확인할 때 사용
클래스 타입이 맞으면 true, 맞지 않으면 false 반환
if(레퍼런스 instanceof 클래스타입) {
//true일때 처리할 내용, 해당 클래스 타입으로 down casting
}
if(c instanceof Sonata) {
((Sonata)c).moveSonata();
} else if (c instanceof Avante){
((Avante)c).moveAvante();
} else if (c instanceof Grandure){
((Grandure)c).moveGrandure();
}
public void ex4() {
// 다운캐스팅 주의사항
Car c1 = new Tesla();
// Car c1 = new Spark(); 이면 성공 출력
// Spark s1 = (Spark)c1;
// 실행 시 오류
// ClassCastException : 형변환 예외
// -> c1이 참조하는 객체는 Tesla 인데
// Spark 참조변수로 Tesla 를 참조하려고하면 문제 발생.
// 해결방법 : instanceof 와 같이 사용하면 됨
// 예외 처리
if(c1 instanceof Spark) {
Spark s1 = (Spark)c1; // 다운캐스팅
System.out.println("성공");
} else {
System.out.println("실패. Spark 타입이 아님");
}
}
실제 실행할 메소드 코드와 호출하는 코드를 연결 시키는 것
프로그램이 실행되기 전 컴파일 단계에서 메소드와 메소드 호출부를 연결
(ctrl F11 누르기 전)
컴파일 시 정적 바인딩된 메소드를 실행할 당시의 객체 타입을 기준으로 바인딩 되는 것
바인딩 (Binding)
실제 실행할 메소드 코드와 호출하는 코드를 연결시키는 것
public void ex5() {
Car c1 = new Car("경유엔진", "경유", 8);
System.out.println(c1.getEngine());
Car 객체에 있는 getEngine() 메소드를 호출 == 바인딩
edu.kh.poly.ex1.model.vo.Car.getEngine()
프로그램 "실행 전"
-> 컴파일러는 getEngine() 메소드가 Car 에 있는 걸로 인식해서
c1.getEngine() 호출코드와
edu.kh.poly.ex1.model.vo.Car.getEngine() 메소드 코드를 연결
-> 정적 바인딩
다형성 적용 시 바인딩
Car c2 = new Spark("경차엔진", "휘발유", 4, 0.5);
// 업캐스팅 적용 -> 부모 부분만 참조 가능한 상태
System.out.println(c2.toString());
// edu.kh.poly.ex1.model.vo.Car.toString()
// 실행은 Spark 에 있는 toString()이 나옴
// 경차엔진 / 휘발유 / 4 / 0.5
// 참조변수 c2가 Car 타입이므로
// toString()도 Car 의 toString() 호출 - 정적바인딩
// 하지만 실행해보면 자식(Spark)의 toString()이 호출되는 것을 확인
// -> 컴파일 시에는 부모(Car)와 바인딩 == 정적바인딩
// -> "실행 시"에는 자식(Spark)의 오버라이딩된 메소드와 바인딩 == 동적바인딩
}
몸체 없는 메소드를 포함한 클래스(미완성 설계도)
추상 클래스일 경우 클래스 선언부에 abstract 키워드 사용
[접근제한자] abstract class 클래스명 {}
몸체 없는 메소드
추상 메소드의 선언부에 abstract 키워드 사용
상속 시 반드시 구현해야 하는, 오버라이딩이 강제화되는 메소드
부모(추상클래스 - 추상메서드)
몸체가 없다는 건 메서드 정의가 안됐다는 거
추상클래스 정의를 자식에서 함 (overriding 강제화)
[접근제한자] abstract 반환형 메소드명(자료형 변수명);
// {} 없음
public class A { // 에러 뜸
// public abstract class A 로 바꿔줘야함
public abstract void ex1();
// 추상 클래스에는 메소드 없어도 됨
}
상속 받은 자식에게 공통된 멤버 제공.
일부 기능의 구현을 강제화(공통적이나 자식 클래스에 따라 재정의 되어야 하는 기능).
부모
public abstract class A {
public abstract void ex1();
}
자식
public class B extends A {
// ex1() 오버라이딩하라고 에러 뜸
@Override
public void ex1() {
syso("정의")
}
}
자식2
public class C extends A {
// ex1() 오버라이딩하라고 에러 뜸
@Override
public void ex1() {
syso("얘도 정의")
}
}
Animal 클래스 (추상 클래스) (부모)
추상 클래스(abstract class)
1. 미완성 메서드 (추상메서드) 를 보유하고 있는 클래스
2. 객체로 만들면 안되는 클래스
-> 여러 타입들을 관리하기 위한 상위 타입의 목적, 객체로 생성하여 사용하기 위한 목적이 아니다.
추상클래스는 new 연산자를 통해 직접적인 객체 생성은 불가능하지만 상속 받은 자식객체 생성 시 내부에 부모 부분이 생성될 때 사용된다.
(heap 영역 자식 클래스 안에 생기는 부모 생성할 때 필요)
package edu.kh.poly.ex2.model.vo;
public abstract class Animal {
// 속성(필드)
private String type; // 종/과
private String eatType; // 식성
// 기능(생성자 + 메서드)
public Animal() {}
public Animal(String type, String eatType) {
super();
this.type = type;
this.eatType = eatType;
}
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;
}
@Override
public String toString() {
return type + " / " + eatType;
}
추상 메서드
공통적인 기능만 추출해야함
동물의 공통 기능 추출(추상화)
-> 동물은 공통적으로 먹고, 숨 쉬고, 움직이지만 어떤 동물이냐에 따라 그 방법이 다름
-> 해당 클래스에 메소드를 정의할 수 없다.
--> 미완성 상태로 만들어서 상속받은 자식이 해당 메서드를 자식 본인에게 맞는 정의를 하도록 오버라이딩 강제화 시킴
--> 추상메서드(abstract method)로 작성
// 먹다
public abstract void eat(); // {} 없어야함 추상메서드
// 숨쉬다
public abstract void breath();
// 움직이다
public abstract void move();
}
Person 클래스 Animal의 자식
클래스 만들고 상속 하면 빨간줄 생김
The type Person must implement the inherited abstract method Animal.move()
추상 메서드 구현 강제화
Animal 의 추상메서드를 오버라이딩 하지 않으면 오류 발생 -> 강제화
package edu.kh.poly.ex2.model.vo;
public class Person extends Animal {
private String name;
// 생성자
public Person() {}
public Person(String type, String eatType, String name) {
super(type, eatType);
this.name = name;
}
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("코와 입으로 숨쉰다.");
}
@Override
public void move() {
System.out.println("두발로 걷는다.");
}
@Override
public String toString() {
return "Person : " + super.toString() + " / " + name;
}
}
Fish 클래스 Animal 클래스의 자식
package edu.kh.poly.ex2.model.vo;
public class Fish extends Animal {
// ctrl alt shift v
@Override
public void eat() {
System.out.println("입을 뻐끔뻐끔 거리면서 먹는다.");
}
@Override
public void breath() {
System.out.println("아가미 호흡을 한다.");
}
@Override
public void move() {
System.out.println("꼬리로 헤엄치며 움직인다.");
}
public Fish() {}
public Fish(String type, String eatType) {
super(type, eatType);
}
@Override
public String toString() {
return "Fish : " + super.toString();
}
}
AbstractService 클래스
Animal a1 = new Animal(); 작성하면
Cannot instantiate the type Animal (객체화 할 수 없음)
클래스 : 객체의 속성, 기능을 정의한 것(일종의 설계도)
추상 클래스 : 미완성 메서드를 포함한 클래스(미완성 설계도)
-> 미완성 설계도로는 객체를 만들 수 없다. -> 오류발생
-> Animal 을 상속 받아 미완성 부분을 구현한 클래스를 이용해 객체 생성
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() {
// 추상클래스를 상속받은 자식 객체 생성
Person p1 = new Person();
p1.setName("홍길동");
// 상속 받은 기능 호출
p1.setType("척추동물");
p1.setEatType("잡식");
p1.eat();
p1.breath();
p1.move();
Fish f1 = new Fish();
f1.eat();
f1.breath();
f1.move();
}
추상클래스는 객체로 만들 수 없다.
--> 하지만 "참조변수"로는 사용할 수 있다.
Animal a1 = new Animal(); // 안됨
사람 -> 동물 / 물고기 -> 동물
Animal a1 = new Person(); // 가능 다형성 중 업캐스팅
Animal a2 = new Fish(); // 다형성 중 업캐스팅 적용
public void ex2() {
Animal[] arr= new Animal[2];
// Animal 참조변수 배열 선언 및 할당
arr[0] = new Person("사람", "잡식", "김사람");
// Animal 부모 = Person 자식(다형성 중 업캐스팅)
arr[1] = new Fish("물고기", "잡식");
// Animal 부모 = Fish 자식(다형성 중 업캐스팅)
// 바인딩 확인
for(int i = 0 ; i < arr.length ; i++) {
// arr[i] == Animal 참조변수
arr[i].eat();
arr[i].breath();
System.out.println(arr[i].toString());
}
}
void edu.kh.poly.ex2.model.vo.Animal.eat() - 정적 바인딩
프로그램 실행 시
참조하고 있는 자식 객체의 오버라이딩된 eat(), breath(), toString()
메소드 수행
-> 동적 바인딩 : 부모타입 참조변수로 메소드를 호출했지만
자식 타입의 오버라이딩된 메소드가 수행된다.
클래스 간의 접점 : 클래스가 공통적으로 가져야하는 필드, 메서드를 모아두고 상속
UI(User Interface) : 사용자와 접점을 이룰 수 있는 것 (HTML,CSS,JavaScript)
상수형 필드와 추상 메소드만을 작성할 수 있는 추상 클래스의 변형체
메소드의 통일성을 부여하기 위해 추상 메소드만 따로 모아놓은 것으로 상속 시 인터페이스 내에 정의된 모든 추상메소드 구현해야 함
(추상메서드와 상수만 가질 수 있음)
[접근제한자] interface 인터페이스명 { // class 아님
//상수도 멤버로 포함할 수 있음
public static final 자료형 변수명 = 초기값;
//추상 메소드만 선언 가능
[public abstract] 반환자료형 메소드명([자료형 매개변수]);
//public abstract가 생략되기 때문에
//오버라이딩 시 반드시 public 표기해야 함
}
다형성을 이용하여 상위 타입 역할(자식 객체 연결)
인터페이스 구현 객체에 공통된 기능 구현 강제화 (== 구현 객체간의 일관성 제공)
공동 작업을 위한 인터페이스 제공

상속
- (자식클래스의) 공통된 부분을 추출하여 부모클래스를 만드는 것
- → 공통된 필드, 메서드를 가진 클래스를 만들고, 작성된 코드를 자식들이 물려받아 사용.
- → 코드길이 감소, 코드 중복 제거, 재사용성 증가, 자식에 대한 일관된 규칙 제공
[일반 클래스] 상속
- 부모 클래스도 객체로 만들수 있어야 하는 경우
[추상 클래스] 상속
- 연관된 클래스의 공통점을 묶되, 부모클래스는 객체로 만들 수 없는 경우
- + 일부 미완성 클래스(abstract 메소드 0개 이상 포함)
- EX) Animal 클래스 → 동물 객체는 어떤 동물인가? eat(), breath() 는 어떻게 수행되는가? → 알 수 없음
- 하지만, 동물의 공통된 기능명은 알고 있음.
[인터페이스] 상속 : 접점
- 연관성이 낮거나 없는 클래스에게 공통된 기능을 제공할 때 사용.
- ex) 키보드, 마우스, 스캐너, 카메라, 기울기 센서 (공통점 : 입력장치)
- 우연히도 입력이라는 기능을 가지고 있음! → 각각의 용도는 다르지만 입력이라는 공통된 기능명이 있음.
- 입력이라는 접점!
- \+ 모든 필드가 묵시적(암묵적) public static final → ex) public static final double PI = 3.141592;
- (public static final) double PI = 3.141592; (묵시적)
- \+ 모든 메서드가 묵시적으로 public abstract(추상메서드) ⇒ 같은 이름을 제공할 뿐이지, 상세한 기능 제공은 하지 않는다.
- ex) (public abstract) void input()
- // input이라는 이름을 자식에게 제공할 뿐, 상세한 기능은 자식이 알아서 오버라이딩 해라! 그런데 추상메서드니까 오버라이딩 강제화