본문 바로가기
백엔드/자바

자바 - 14

by study_yeon 2023. 5. 17.

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) 입력받은 첫 번째 문자만 반환하여 문자로 반환

'백엔드 > 자바' 카테고리의 다른 글

자바 - 16  (0) 2023.05.19
자바 - 15  (0) 2023.05.18
자바 - 13  (0) 2023.05.16
자바 - 12  (0) 2023.05.14
자바 - 11  (0) 2023.05.11