JAVA_09_다형성

송지윤·2024년 2월 16일

Java

목록 보기
15/22

다형성 (Polymorphism)

객체 지향 언어의 특징 중 하나로 '다양한 형태를 갖는다'라는 뜻으로 하나의 행동으로 여러 가지 일을 수행하는 개념.
상속을 이용한 기술로 부모 클래스 타입 참조변수 하나로 상속 관계에 있는 여러 타입의 자식 객체를 참조할 수 있는 기술

부모클래스 변수명 = new 자식클래스();
둘이 자료형이 다름

원래 같은 자료형끼리만 연산 가능하다는 연산 규칙때문에 다른 자료형끼리 연산 불가
이걸 가능하게끔 해주는 게 상속

부모 class Shape에
Shape shape = new Circle();
Shape shape = new Triangle();

클래스 형변환

업 캐스팅(Up Casting)

클래스 자료형을 형변환하는 거
상속 관계에 있는 부모, 자식 클래스 간에 부모타입의 참조형 변수모든 자식 타입의 객체의 주소를 참조할 수 있음

// 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 안에 있는 애들 사용하지 못함

다운 캐스팅(Down Casting)

자식 객체의 주소를 받은 부모 참조형 변수를 가지고 자식의 멤버를 참조해야 할 경우, 부모 클래스 타입의 참조형 변수를 자식 클래스 타입으로 형변환 하는 것
자동으로 처리되지 않기 때문에 반드시 자식 타입을 명시하여 형변환(강제 형변환)

//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());
		
	}

instanceof 연산자

현재 참조형 변수가 어떤 클래스 형의 객체 주소를 참조하고 있는지 확인할 때 사용
클래스 타입이 맞으면 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 키워드 사용

[접근제한자] abstract class 클래스명 {}

추상 메소드(abstract method)

몸체 없는 메소드
추상 메소드의 선언부에 abstract 키워드 사용
상속 시 반드시 구현해야 하는, 오버라이딩이 강제화되는 메소드
부모(추상클래스 - 추상메서드)
몸체가 없다는 건 메서드 정의가 안됐다는 거
추상클래스 정의를 자식에서 함 (overriding 강제화)

[접근제한자] abstract 반환형 메소드명(자료형 변수명);
// {} 없음

특징

  1. 미완성 클래스(abstract 키워드 사용)
    자체적으로 객체 생성 불가 → 반드시 상속하여 객체 생성(자식들이 객체 생성)
  2. abstract 메소드가 포함된 클래스는 반드시 abstract 클래스
    abstract 메소드가 없어도 abstract 클래스 선언 가능
public class A { // 에러 뜸
// public abstract class A 로 바꿔줘야함
	public abstract void ex1();
    // 추상 클래스에는 메소드 없어도 됨
}
  1. 클래스 내에 일반 변수, 메소드 포함 가능
  2. 객체 생성은 안되지만 참조형 변수 타입으로는 사용 가능

장점

상속 받은 자식에게 공통된 멤버 제공.
일부 기능의 구현을 강제화(공통적이나 자식 클래스에 따라 재정의 되어야 하는 기능).
부모

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()
메소드 수행
-> 동적 바인딩 : 부모타입 참조변수로 메소드를 호출했지만
자식 타입의 오버라이딩된 메소드가 수행된다.

인터페이스(Interface)

클래스 간의 접점 : 클래스가 공통적으로 가져야하는 필드, 메서드를 모아두고 상속

UI(User Interface) : 사용자와 접점을 이룰 수 있는 것 (HTML,CSS,JavaScript)

상수형 필드추상 메소드만을 작성할 수 있는 추상 클래스의 변형체
메소드의 통일성을 부여하기 위해 추상 메소드만 따로 모아놓은 것으로 상속 시 인터페이스 내에 정의된 모든 추상메소드 구현해야 함
(추상메서드와 상수만 가질 수 있음)

[접근제한자] interface 인터페이스명 { // class 아님
	//상수도 멤버로 포함할 수 있음
	public static final 자료형 변수명 = 초기값;

	//추상 메소드만 선언 가능
	[public abstract] 반환자료형 메소드명([자료형 매개변수]);
	//public abstract가 생략되기 때문에
	//오버라이딩 시 반드시 public 표기해야 함
}

특징

  1. 모든 인터페이스의 메소드는 묵시적으로 public abstract
  2. 변수는 묵시적으로 public static final (상수)
  3. 객체 생성은 안되나 참조형 변수로는 가능 (다형성)

장점

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

추상클래스와 인터페이스

정리

  • 상속
    - (자식클래스의) 공통된 부분을 추출하여 부모클래스를 만드는 것
    - → 공통된 필드, 메서드를 가진 클래스를 만들고, 작성된 코드를 자식들이 물려받아 사용.
    - → 코드길이 감소, 코드 중복 제거, 재사용성 증가, 자식에 대한 일관된 규칙 제공

  • [일반 클래스] 상속
    - 부모 클래스도 객체로 만들수 있어야 하는 경우

  • [추상 클래스] 상속
    - 연관된 클래스의 공통점을 묶되, 부모클래스는 객체로 만들 수 없는 경우
    - + 일부 미완성 클래스(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이라는 이름을 자식에게 제공할 뿐, 상세한 기능은 자식이 알아서 오버라이딩 해라! 그런데 추상메서드니까 오버라이딩 강제화

0개의 댓글