2023.05.16
키워드 : 인터페이스, 추상화, 업캐스팅, 다운캐스팅
● 객체지향 4(5)대 특징
- 추상화
- 캡슐화(정보은닉)
- 상속성
- 다형성
▷ 다형성 : 하나의 객체가 여러가지 자료형을 가질 수 있는 것(메소드 안의 내용이 변경되어야함)
- 비유 : 달린다(메소드)라는 것은 대상마다 구현하는 방법이 모두 다르다(사람, 자전거, 자동차, 동물 등)
* 다르게 표현하는 방법 : 오버로딩(매개변수를 다르게 함), if문(메소드를 다르게 호출)
-> 유지보수가 어려움
* 달리는 대상(객체)에 따라 자동으로 맞는 메소드가 호출되게 개발
-> 자전거.달린다 / 자동차.달린다
-> 표준화 하기(인터페이스)
● 인터페이스
- 객체의 사용방법을 정의한 타입
- 모든 메소드가 public(기본설정값)
- 개발코드와 객체가 서로 통신하는 접점 역할
- implements를 통해 상속
▷ 목적
인터페이스는 실제 메소드를 정의하고 구현하는 것이 아니라 메소드의 헤더부분(시그니처)만 선언하며
이 메소드를 실제 정의하는 클래스에서 메소드 본체를 완성
호환성을 보장하고 이 기능(메소드)은 반드시 구현하여야 한다는 것을 프로그램 차원에서 보증함
-> 작업지시서 또는 주문확인서
▷ 중요한 이유
반드시 구현되어야 하는 메소드를 알려줌(사전 예방 가능)
-> 프로그램차원에서 안정성 확보
자바는 복잡미묘한것(모호성)을 해결하기 위해 단일상속만 가능
-> 다형성(인터페이스는 다중상속을 구현할 수 있음)
클래스를 범위 내에서 작동하게 지정해줌
● 인터페이스 실습1
1. 패키지 - inherit.myinterface
2. 인터페이스 - ICalc (class를 interface로 변경 | interface 생성)
public interface ICalc {
// 메소드의 헤더부분(시그니처)만 선언
double add(double x, double y);
double sub(double x, double y);
double mul(double x, double y);
double div(double x, double y);
}
3. 클래스 - CCalc
- calc객체에 add, sub, mul, div 메소드 구현
package inherit.myinterface;
public class CCalc implements ICalc { // implements 구현한다
// 필수 메소드
public double add(double x ,double y) {
return x + y;
}
public double sub(double x, double y) {
return x - y;
}
public double mul(double x, double y) {
return x * y;
}
public double div(double x, double y) {
return x / y;
}
}
- implements를 하면 반드시 add, sub, mul, div 메소드를 구현해야함
4. 실행클래스 - CalcApp
package inherit.myinterface;
public class CalcApp {
public static void main(String[] args) {
// CCalc 객체 생성하여 calc변 수에 참조주소 저장
CCalc calc = new CCalc();
// calc객체의 add 메소드 호출
double retVal = calc.add(100.0, 200.0);
// System.out.println("retVal = " + retVal); // 유틸클래스 활용 전
calc.p("retVal = "); // 유틸클래스 활용
calc.pl("" + retVal);
ICalc iCalc = new CCalc(); // 업캐스팅(인터페이스 크기로 한정)
retVal = iCalc.sub(100.0, 50.0);
System.out.println("retVal = " + retVal);
// 필수메소드만 사용 가능(4개)
//iCalc.p("retVal = "); 에러
//iCalc.pl("" + retVal); 에러
}
}
* calc객체에 add, sub, mul, div 메소드가 구현되어 있는가 검증하는 법
1) calc.을 하였을때 메소드가 보이는지 확인
2) 인터페이스를 활용하면 보장 됨 : 1번 방법으로 확인하기 전에 확인하는 법
▷ 인터페이스의 효과
- 필수메소드만 사용 가능(4개) : Calc에 있는 메소드 사용 불가
- 인터페이스도 테이터형으로 취급
* 최고조상
인터페이스 > 오브젝트 > 부모클래스 > 자식클래스
- 인터페이스는 추상클래스와 유사
* 참고
- 데이터를 보관할 창고(=멤버변수)
- 멤버변수를 관리할 메소드(=클래스)
* 다형성
자바는 상속 하나만 가능(단일상속)
● 타입캐스팅(타입변환)
1. 업캐스팅
Wearable[] wa = new Wearable[2];
/* 업캐스팅 : 자식객체를 부모타입으로 변경하는 것 */
wa[0] = new Headphone();
wa[1] = new Wearablecomputer();
- 슈퍼클래스 타입으로 서브클래스를 가리킨다
- 서브클래스 객체를 슈퍼클래스 타입으로 타입 변환
- 객체 내에 슈퍼클래스의 멤버만 접근 가능
2. 다운캐스팅
/* 다운캐스팅 : 업캐스팅했던 객체를 본래 자기타입으로 다운한다 */
((Headphone)wa[0]).setVolume(0); // Headphone으로 형변환 : 괄호를 하나의 구성으로 봄
((Wearablecomputer)wa[1]).reset();
// 다운캐스팅은 원래 자기 형식이어야 한다
// ((Headphone)wa[1]).setVolume(10); 형변환 실행에러
● 추상화 실습1
1. 유틸클래스 만들기 MyIO
package inherit.myinterface;
public class MyIO {
public static void p(String msg) {
System.out.print(msg);
}
public static void pl(String msg) {
p(msg + "\n");
}
}
2. 인터페이스 ISpeed
package inherit.myinterface;
public interface ISpeed {
public void speedUp(int speedValue);
public void speedDown(int speedValue);
public void stop();
}
- 메소드 헤더(시그니처)만 선언
3. 클래스 Vehicle
package inherit.myinterface;
import static inherit.myinterface.MyIO.*;
public abstract class Vehicle implements ISpeed {
void powerOn() {
pl("전원을 켠다.");
}
void powerOff() {
pl("전원을 끈다.");
}
abstract void run(); // abstract : 불완전한 클래스(추상화), 메소드를 선언만 함
public void stop() {
p("Vehicle이 멈춥니다");
}
}
- MyIO import하기
* import static :
불러오는 클래스 안의 모든 static메소드를 현재 클래스의 메소드인 것처럼 불러오게 해주는 지시어
- abstract에서 인터페이스를 상속받은 이유는?
-> ISpeed는 인터페이스라 선언부 구현이 필요함
4. Vehicle 상속클래스 (Car, Ship, AirPlane)
- Vehicle의 추상화메서드인 run()을 구현해주어야함
package inherit.myinterface;
import static inherit.myinterface.MyIO.*;
public class Car extends Vehicle {
@Override
void run() { // Vehicle 클래스의 run() 구현
pl("달립니다.");
}
public void speedUp(int speedUp) { // ISpeed 인터페이스의 메소드 구현
pl("차의 속력을 높입니다");
}
public void speedDown(int speedDown) {
pl("차의 속력을 낮춥니다");
}
}
- 자체 메소드를 가질 수 있음
package inherit.myinterface;
import static inherit.myinterface.MyIO.*;
public class AirPlane extends Vehicle{
@Override
void run() {
pl("달립니다.");
}
void fly() { // 자체 메소드
pl("하늘을 납니다.");
}
@Override
public void speedUp(int speedUp) {
pl("비행기 속도를 높입니다.");
}
@Override
public void speedDown(int speedDown) {
pl("비행기 속도를 낮춥니다.");
}
}
5. 실행클래스(CarAirplaneShipApp)
package inherit.myinterface;
import static inherit.myinterface.MyIO.*;
public class CarAirplaneShipApp {
public static void main(String[] args) {
Car car = new Car(); // 객체생성
car.powerOn(); // 메소드 사용
car.run();
car.powerOff();
AirPlane airPlane = new AirPlane();
airPlane.fly();
}
}
// 결과값
전원을 켠다.
달립니다.
전원을 끈다.
하늘을 납니다.
▷ 추상화 클래스를 상속받을 경우
- abstract : 불완전한 클래스(추상화), 메소드를 선언만 함
- >별도의 클래스를 활용하여 메소드를 구현해줘야 함
- 인터페이스와 같이 최고조상급
▷ abstract에서 인터페이스를 상속할 경우
- 나머지 상속 클래스에서 오류가 생김
- ISpeed는 인터페이스임(선언부 필요)
* 추상클래스
추상 클래스(abstract class)는 추상화를 이용한 클래스로 추상 메서드를 포함
(추상 메서드: 구현 내용 없이, 선언만 있는 메서드)
* 추상 클래스는 추상 메서드를 포함하고 있어, 직접 객체를 생성하여 사용할 수 없다.
->상속을 통해 자식(하위)클래스에서 추상 메서드를 반드시 구현(오버라이딩)하여 사용
* 추상클래스와 인터페이스의 차이
인터페이스는 추상 클래스보다 추상화 정도가 높아서 메소드의 헤더(시그니처)만 멤버로 가진다.
추상 클래스는 몸통을 가지고 있는 일반 멤버 메서드, 멤버 변수도 구성원으로 가질 수 없다.
추상 클래스는 extends를 이용하여 상속, 인터페이스는 implements를 이용하여 상속 받는다.
추상 클래스는 '미완성 클래스', 인터페이스는 '기본 설계도'
* 인터페이스 파일 이름 짓기
1. I로 시작
2. ~~wearable
* 다운캐스팅 : 업캐스팅했던 객체를 본래 자기타입으로 다운한다
- 업캐스팅된 객체에 본인이 생성한 메소드가 없어 사용하지 못한다
-> 다시 나의 타입으로 다운하는 다운캐스트 활용하기
- 괄호를 하나의 구성(인수)으로 봄 - 명시적으로 표현
* ((Headphone)wa[1]).setVolume(10); 이 가능한지 디버그로 확인해보기
- 다운캐스팅은 원래 자기 형식이어야 한다
-> wa[1]은 Wearablecomputer 클래스를 Wearable 클래스로 업캐스팅 한 것이라 Headphone으로 변환 불가
- 생성자가 없으면 기본생성자가 생김
-> 형변환 에러
* 인터페이스
- 꼭 구현을 해야하는 기능을 담음
* 계산기 클래스
패키지 : inherit.myinterface.calc
1. 모든 변수는 직접접근 금지 (속성함수 활용)
package inherit.myinterface.calc;
public abstract class AbstractCalc implements Calcable{
private int num1; // 피연산변수
private int num2;
private char oper; // 산술연산자기호
// 숫자 값을 읽기
public int getNum1() {
return num1;
}
// 숫자 값을 설정
public void setNum1(int num1) {
this.num1 = num1;
}
public int getNum2() {
return num2;
}
public void setNum2(int num2) {
this.num2 = num2;
}
public char getOper() {
return oper;
}
public void setOper(char oper) {
this.oper = oper;
}
public abstract void calc();
}
2. 필수기능(4칙연산)은 인터페이스 활용
- 곱셈(직접구상),나눗셈은 추상클래스 abstract
package inherit.myinterface.calc;
public interface Calcable {
// 사칙연산은 전부 필수 구현하기
int add(int x, int y);
int sub(int x, int y);
int mul(int x, int y);
int div(int x, int y);
}
4. 구현클래스 Calc
* 기능
- num1 연산자(+ - / *) num2 = 결과
- 연산자에 따라 계산할 함수를 부른다
package inherit.myinterface.calc;
import static inherit.myinterface.MyIO.*;
// src : 최상위 폴더
/* 자식 클래스 */
public class Calc extends AbstractCalc {
@Override
public int add(int x, int y) {
return x + y;
}
@Override
public int sub(int x, int y) {
return x - y;
}
@Override
public int mul(int x, int y) {
return x * y;
}
@Override
public int div(int x, int y) {
return x / y;
}
@Override
public void calc() { // 인터페이스 구현
/* 1. 다중 if문
if (oper == '+') {
} else if (oper == '-') {
} else if (oper == '*') {
} else if (oper == '/') {
}*/
var oper = getOper();
var num1 = getNum1();
var num2 = getNum2();
/*2. switch 문 */
switch (oper) {
case '+' :
pl("num1" + '+' + "num2" + " = " + add(num1, num2));
break;
case '-' :
pl(num1 + oper + num2 + " = " + sub(num1, num2));
break;
case '*' :
pl(num1 + oper + num2 + " = " + mul(num1, num2));
break;
case '/' :
pl(num1 + oper + num2 + " = " + div(num1, num2));
break;
default :
p("연산자를 확인해주세요.");
break;
}
}
}
5. 실행클래스 CalcApp
package inherit.myinterface.calc;
import static inherit.myinterface.MyIO.*;
import java.util.Scanner;
public class CalcApp {
public static void main(String[] args) {
// 계산기 실체화
Calc cal = new Calc();
Scanner scan = new Scanner(System.in);
p("첫 번째 숫자 : ");
cal.setNum1(scan.nextInt());
p("연산자 : ");
cal.setOper(scan.next().charAt(0));
p("두 번째 숫자 : ");
cal.setNum2(scan.nextInt());
cal.calc();
}
}
interface : 정의만 되어있음
abstract : 실체가 없다(상속 시 점선으로 표현)
구현클래스 : abstract에서 구현이 되지 않은 부분들을 구현함
* charAt : 문자열을 문자로 변환
setOper은 문자임, next()은 문자열로 입력을 받아서 에러가 발생
-> scan.next().charAt(0) 입력받은 첫 번째 문자만 반환하여 문자로 반환