[자바] 인터페이스 연습01

오늘·2021년 3월 17일
0

문제풀기

목록 보기
21/24

interface의 역할

: 객체의 사용 방법을 정의한 타입
: 객체의 교환성을 높여주기 때문에 다형성을 구현하는 매우 중요한 역할

-왜 직접 객체의 메소드를 호출하지 않고, 중간에 인터페이스를 두는 걸까?
: 개발 코드를 수정하지 않고, 사용하는 객체를 변경할 수 있도록 하기 위해서
: 하나의 객체가 아니라 여러 객체들과 사용이 가능하므로 어떤 객체를 사용하느냐에 따라서 실행 내용과 리턴값이 다를 수 있다
-> 개발 코드 측면에서는 코드 변경 없이 실행 내용과 리턴값을 다양화할 수 있는 장점

interface에서 가능한 멤버 구성

  1. 상수필드
    : 상수 = 절대로 변하지 않는것
    ex) 지구 반지름, 지구 넓이, 파이값...
    : Interface 에서는 기본 필드형이 상수이기 때문에 앞에 static final을 생략 가능하다 -> 따라서 변수 선언과 동시에 초기화가 필수
    : 상수명은 대문자로 작성하고, 서로 다른 단어로 구성되어 있을 경우에는 언더바로 연결하는 것이 관례
static final double PI = 3.141592;
static final int NUMBER2021 = 210;
static final String SCHOOL_NAME = "AB";

// static final 표기 생략
int A = 10;
doubld B = 3.0;
  1. 추상메소드
    : abstract 리턴타입명 메소드명(매개변수);
    : abstract 표기 생략도 가능하다
    : Interface에서 추상 메소드를 작성해주었다면, 이 interface를 부모로 사용한 자식 객체들에서 모두 오버라이딩이 필수
abstract void method();
abstract int method1(double a, int b);
abstract boolean method2();
abstract void abMethod();

// abstract 표기 생략
String method3();
  1. 디폴트 메소드
    : default 메소드는 자식 객체들에서 오버라이딩이 필수 아님. 받아갈 객체만 오버라이딩 해 받아가면 된다
default void soundMethod() {
	System.out.println("인터페이스의 default 메소드임");
}

default void bMethod() {
}
  1. 정적메소드
    : 코드 실행문 작성 가능
public static void sMethod() {
}

static void cMethod() {
}

인터페이스끼리 상속 가능한가?

가능하다

public interface InterEx01 {	}
public interface InterEx02 extends InterEx01 {	}

다중 상속도 가능

public interface InterEx01 {	}
public interface InterEx02 {	}

public interface InterEx03 extends InterEx01, InterEx02 {	}

일반 클래스와는 상속(extends)가 아니라 연결(implements)이라 한다
다만 이건 단일 연결만 가능

public interface InterEx01 {	}
public interface InterEx02 {	}

// 가능
public class InterfaceEx implements InterEx01 {    }
// 불가능
public class InterfaceEx implements InterEx01, InterEx02 {    }
public class InterfaceEx implements InterEx01 implements InterEx02 {    }

상속과 연결을 같이 쓰는것도 가능하다

public class InterfaceEx extends Unit implements InterEx01, InterEx02 {	}

조립(호출) 클래스에서 인터페이스를 사용하는 모습

  1. 상수는 그냥 불러와 사용 가능 -> static으로 선언되었기 때문에
    인터페이스명.상수명
System.out.println(InterEx.PI);
System.out.println(InterEx.NUMBER2021);
System.out.println(InterEx.SCHOOLNAME);
System.out.println(InterEx02.A);
  1. static 메소드도 그냥 호출로 사용하면 된다
    : static이라 다른 곳에서 오버라이딩은 불가
    : 객체 생성(new) 된 것으로 불러오기 안됨
    : 인터페이스명.메소드명 으로 호출가능
InterEx.sMethod();
InterEx02.cMethod();
  1. 추상메소드, default 메소드를 사용하려면?
    1) 일반클래스에 인터페이스 연결
    -> Interface는 new를 이용한 객체생성이 되지 않음
    2) 일반 클래스를 객체화한다
    3) 사용한다
ClassInterEx ci = new ClassInterEx();
ci.abMethod();
ci.method();
ci.soundMethod();

-사용하는 모습 보기(리모컨으로 Tv, Lamp 설정바꾸기)

interface 선언하기

-상수필드, 추상메소드, 디폴드 메소드, 정정메소드 모두 사용
-Object클래스의 toString() 메소드도 사용

public interface RemoteControl {
	// 상수 필드
	// 상수명은 대문자로 작성하되
	// 서로 다른 단어로 구성되어 있을 경우 언더바로 연결하는 것이 관례
	// public, static, final을 생략하더라도 자동적으로 컴파틸 과정에서 붙음
	public int MAX_VOLUME = 10;
	public int MIN_VOLUME = 0;
	
	
	
	// 추상 메소드
	// public abstract를 작성해주지 않아도 컴파일 과정에서 자동으로 붙는다 
	public void turnOn();
	public void turnOff();
	public void setVolume(int volume);
	
	
	
	// 디폴드 메소드
	default void setMute(boolean mute) {
		// true면 if 실행, flase면 else 실행
		if(mute) {
			System.out.println(toString() + " 무음 처리합니다");
		} else {
			System.out.println(toString() + " 무음 해제합니다");
		}
	}
	@Override
	String toString();
	
	
	
	// 정적 메소드
	static void changeBattery() {
		System.out.println("건전지를 교환합니다");
	}
}

일반 클래스(= 구현 클래스) Tv, Lamp

interface 클래스를 받아와 구현하는 클래스이기도 하기 때문에 구현 클래스라 하기도 한다.

  1. Tv 클래스
public class Tv implements RemoteControl {
	// 필드
	private int volume;
	
	@Override
	public void setVolume(int volume) {
		if(volume > RemoteControl.MAX_VOLUME) {
			this.volume = RemoteControl.MAX_VOLUME;
		} else if (volume < RemoteControl.MIN_VOLUME) {
			this.volume = RemoteControl.MIN_VOLUME;
		} else {
			this.volume = volume;
		}
		
		System.out.println("현재 볼륨 : " + volume);
		if (volume > 8) {
			System.out.println("청력을 위해 볼륨을 줄이세요");
		}
	}

	@Override
	public void turnOff() {
		System.out.println(toString() + "를 끕니다");
	}

	@Override
	public void turnOn() {
		System.out.println(toString() + "을 켭니다");
	}
	
	@Override
	public String toString() {
		return "TV";
	}
}
  1. Lamp 클래스
public class Lamp implements RemoteControl{
	@Override
	public void setVolume(int volume) {	
	}
	
	@Override
	public void turnOn() {
		System.out.println(toString() + "를 끕니다");
	}
	
	@Override
	public void turnOff() {
		System.out.println(toString() + "을 켭니다");
	}
	
	
	@Override
	public String toString() {
		return "형광등";
	}
	
    	// default 메소드도 받아와 재정의 후 사용하는 것 가능하다
        // 이 경우 main에서 호출시
        // Interface의 것이 아닌 재정의된 default 메소드가 출력된다
	@Override
	public void setMute(boolean mute) {
		if(mute) {
			System.out.println(toString() + "을 새로 갈았습니다");
		} else {
			System.out.println(toString() + "이 오래 되었습니다");
		}
	}
}

호출하고 출력하는 Main 클래스

public class Audio {
	public static void main(String[] args) {
		// 상수 사용하기 -> 인터페이스명.상수명
		// 맥스볼륨 출력하기
		System.out.println(RemoteControl.MAX_VOLUME);
		// 민 볼륨에 5 더해서 출력하기
		int num = RemoteControl.MIN_VOLUME + 5;
		System.out.println(num);
		System.out.println("----------------------------");
		
		// 추상메소드 사용하기
		// 일반 클래스에 연결 후 오버라이딩해 출력 가능
		Tv tv = new Tv();
		tv.turnOn();
		tv.setVolume(50);
		tv.turnOff();
		
		Lamp lamp = new Lamp();
		lamp.turnOn();
		lamp.setMute(true);
		lamp.turnOff();
		System.out.println("----------------------------");
		
		// 디폴드 메소드 사용하기
		tv.setMute(true);
		lamp.setMute(true);
		
		// 정적 메소드 사용하기
		// static이라 다른 곳에서 오버라이딩 불가
		// 객체 생성된 것으로 불러오기 안됨
		// 클래스명.메소드명 으로 호출가능
		RemoteControl.changeBattery();
		// tv.changeBattery(); <- 안됨
	}
}

실행모습


연결(상속) 연습하기

아래 그림과 같은 관계를 구현해보자

  1. Object 클래스는 작성하지 않아도 기본적으로 존재하는 상위 클래스이기 때문에 코드 구현 x
  2. Unit 클래스 구현
// 부모 클래스
public class Unit {
}
  1. 인터페이스인 Movable
public interface Movable {
	// 추상 메소드 하나 선언해줌
	void move();
}
  1. 인터페이스인 Attackable
public interface Attackable {
	// 추상 메소드 하나 선언해줌
	void attack();
}
  1. Movable과 Attackable를 상속받는 Fightable
// 인터페이스끼리는 extends 를 사용해 연결 -> 다중연결도 가능
// 일반클래스와 인터페이스는 implements 사용해 연결 -> 단일 연결만 가능
public interface Fightable extends Movable, Attackable {
	void fight();
}
  1. Unit과 Fightable을 가져오는 Fighter
// 클래스의 상속도 받고
// 클래스에 인터페이스도 연결하겠다면
// 섞어 사용하는 거 가능
public class Fighter extends Unit implements Fightable, Inter2 {
	// 추상메소드를 가지고 있는 인터페이스
    	//클래스와 연결할 시 에러를 수정하려면
	// 추상메소드를 오버라이딩 해야한다
	@Override
	public void move() {	}
	@Override
	public void fight() {	}
	@Override
	public void attack() {	}
	
	@Override
	public void in1() {	}
	@Override
	public void in2() {	}
}
  1. 호출해 사용해보는 Main
public class FighterMain {
	public static void main(String[] args) {
		Fighter f = new Fighter();
		
		// instanceof 
		// : 앞에 있는 객체로 뒤에있는 객체를 가리킬 수 있습니까
		// : 앞에있는 객체는 뒤에있는 객체의 자손입니까
		if (f instanceof Unit) {
			System.out.println("f는 Unit 클래스의 하위입니다");
		}
		if (f instanceof Fightable) {
			System.out.println("f는 fightable 클래스의 하위입니다");
		}
	}
}

실행결과


인터페이스 타입으로의 형변환?

-다형성
: Animal a = new Cat();
: 하위 클래스의 인스턴스를 상위 타입의 참조변수로 참조하는 것이 가능한 것

-인터페이스 역시 이를 구현한 상위 타입의 클래스라 할 수 있기에
해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며 인터페이스 타입으로의 형변환도 가능
: 인터페이스명 참조변수 = new 구현클래스();

-인터페이스의 장점
: 개발시간을 단축시킬 수 있다.
: 표준화가 가능하다
: 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다
: 독립적인 프로그래밍이 가능하다

  1. 인터페이스 하나 작성
public interface Fightable {
}
  1. 구현하는 일반 클래스 작성. 일반 클래스를 상속받으며 인터페이스와 연결하였다
public class Fighter extends Unit implements Fightable {
}

class Unit{
}
  1. 클래스 조립하기
public class MainClass {
	public static void main(String[] args) {
		// 다형성
		// 부모가 더 큰 타입이라 자동 형변환 된 상황
		// 부모타입 변수 = new 자식타입();
        	// 인터페이스 변수 = new 자식클래스();
		Fightable f = new Fighter();
		
		// 자식타입 변수 = 부모타입변수
		// 작은타입에 큰 타입을 넣어야하니 다운캐스팅(강제 형변환) 필요
		Fighter fi = (Fighter) f;
	}
}

인터페이스 연습

-Parserable인터페이스
: 구문분석을 수행하는 기능을 구현할 목적으로 추상메서드'parse(String fileName)을 정의

-XMLParser 구현 클래스
-HTMLParser구현 클래스

ParserManager클래스의 getParser메서드
: 매개변수로 넘겨 받는 type의 값에 따라 XMLParser인스턴스 또는 HTMLParser인스턴스를 반환

  1. Parserable 인터페이스 작성
public interface Parseable {
	// 구문 분석 작업을 수행
	// 추상 메소드 선언
	public abstract void parse(String fileName);
}
  1. XMLParser, HTMLParser 구현 클래스 작성
public class XMLParser implements Parseable {
	@Override
	public void parse(String fileName) {
		// 구문 분석작업을 수행하는 코드
		System.out.println(fileName + "- XML parsing completed");
	}
}

class HTMLParser implements Parseable {
	@Override
	public void parse(String fileName) {
		// 구문 분석작업을 수행하는 코드
		System.out.println(fileName + "- HTML parsing completed");
	}
}
  1. ParserManager 작성
    if 문에 해당하는지 안하는지에 따라 메모리에 올라가는 자식클래스가 달라지는 모습을 출력으로 확인
public class ParserManager {
	// 리턴타입이 Parseable인 인터페이스
	// getParser의 리턴타입은 Parseable이니 메인에서 Parseable로 받음
	// Parseable parser = ParserManager.getParser("XML");
	public static Parseable getParser(String type) {
		if (type.equals("XML")) {
			// 새롭게 XML파서를 생성해라
			// 힙메모리에 올려라
			return new XMLParser();
		} else {
			Parseable p = new HTMLParser();
			return p;
		}
	}
	
	
	public static void main(String[] args) {
		// Parseable 의 parser에
		// 파서매니저
		Parseable parser = ParserManager.getParser("XML");
		parser.parse("document.xml");
		
		parser = ParserManager.getParser("HTML");
		parser.parse("document2.html");
	}
}

실행결과

0개의 댓글