본문 바로가기

Java 웹 개발

21.09.03 - 웹 개발 입문 18일차

다형성 장단점

- 장점 : 코드가 압도적으로 감소한다

확장성이 좋아지고 결합이 약해진다

후반부로 갈 수록 많이 나오는 기술

- 단점 : 어렵다         

전제조건이 까다롭다 (1. 상속, 2. 재정의)         

오류가 발생한 경우 추적이 어렵다

 

다중상속과 인터페이스

자바의 절대 규칙 : 클래스는 단 하나만 상속이 가능하다. 

왜? 모호한 상황이 생기기 때문에!
그렇다고 다중상속이 필요없다고 생각하는것은 아니다
다중상속은 반드시 필요하지만 오류의 가능성(ex:모호성)이 매우 높은 방법

 

-> 인터페이스는 다중상속이 가능하다

클래스는 최대 1개의 클래스와 무제한 개수의 인터페이스 상속이 가능하다
인터페이스는 implements 키워드로 상속받는다

 

Q. 인터페이스 구현 방법보기

package oop.multi3;

public interface Phone {
	void camera();
	void gallery();
	void call();
	void sms();
}
package oop.multi3;

public class Galaxy21s implements Phone {

	@Override
	public void camera() {
		}

	@Override
	public void gallery() {
		}

	@Override
	public void call() {
		}

	@Override
	public void sms() {
		}

}

 

 

인터페이스의 구조

인터페이스도 클래스의 한 종류이다.
클래스의 구성요소들 중 가질 수 있는 것과 없는 것이 있다.
다중상속에서의 부모 클래스가 목적이기 때문에 "모호함"을 발생시키는 요소들이 전부다 제거되었다.
결론 : 인터페이스는 "상수"와 "추상메소드"를 가질 수 있다.

package oop.multi3;

public interface sample {
	// 멤버 변수 : 생성 불가. 오로지 상수만 정의할 수 있다. 안써도 자동생성
	public static final int a = 10;
	/* public static final */ int b = 20; // 안써도 자동 생성

	// 멤버 메소드 : 일반 메소드는 불가. 추상 메소드만 가능. 안써도 자동생성
	/* public abstract */ void showInfo(); // 안써도 자동 생성

	// 생성자 : 생성자는 가질 수 없다(super 사용 방지 차원)
	// public Sample(){}
}

 

 

Q . 예제로 다중상속 풀이

대형 생필품 매장에서 제품들을 관리하기 위해 클래스 시스템을 구축하려 합니다.
- 관리할 제품들은 다음과 같습니다.
- 컴퓨터(Computer)
- 빵(Bread)
- 정리용 바구니(Basket)
- 우유(Milk)
- 유리접시(Dish)

- 제품들은 각각 다음과 같은 핵심 기준에 의해서 분류됩니다.(단 한 가지의 특성만 적용이 가능합니다)
- 전자제품(Electronic) : 전기에 의해서 구동되는 물건들을 말하며 이름을 설정할 수 있고 전원 관련 기능(on, off)를 가지고 있습니다.
- 식료품(Grocery) : 먹을 수 있는 제품들을 말하며 이름과 유통기한을 설정할 수 있습니다.
- 생활용품(Household) : 일상생활에 필요한 용품들을 말하며 이름을 설정할 수 있습니다.

- 제품들은 각각 다음과 같은 기준에 의해서 추가 분류됩니다.
- 깨질 수 있는 제품(Brokenable) : 충격을 받으면 깨질 수 있는 형태의 물건을 의미하며 따로 기능은 존재하지 않습니다.
- 액체 상품(Liquid) : 액체 형태의 물건을 의미하며 따로 기능은 존재하지 않습니다.

 

상위부터 하위로 구현하기

package oop.multi5;

public abstract class Product {
	protected String name;
	public void setName(String name) {
		this.name = name;
	}
	public String getName() {
		return name;
	}

	public Product(String name) {
		this.setName(name);
	}
}

 

<대분류>

package oop.multi5;

public abstract class Electronic extends Product {
	public Electronic(String name) {
		super(name);
	}

	public abstract void on();

	public abstract void off();
}
package oop.multi5;

public abstract class Grocery extends Product {
	protected String expire;

	public void setExpire(String expire) {
		this.expire = expire;
	}

	public String getExpire() {
		return expire;
	}

	public Grocery(String name, String expire) {
		super(name);
		this.setExpire(expire);
	}
}
package oop.multi5;

public abstract class Household extends Product {

	public Household(String name) {
		super(name);
	}

}

 

<Marking interface>
오직 검사만을 위해 만드는 인터페이스
기능이 하나도 없음

 

package oop.multi5;

public interface Brokenable {

}
package oop.multi5;

public interface Liquid {

}

 

<제품 종류>

package oop.multi5;

public class Computer extends Electronic implements Brokenable {

	public Computer(String name) {
		super(name);
	}

	@Override
	public void on() {
		System.out.println(this.name + " 전원이 켜집니다");
	}

	@Override
	public void off() {
		System.out.println(this.name + " 전원이 꺼집니다");
	}

}
package oop.multi5;

public class Bread extends Grocery{

	public Bread(String name, String expire) {
		super(name, expire);
	}

}
package oop.multi5;

public class Basket extends Household implements Brokenable {

	public Basket(String name) {
		super(name);
	}

}
package oop.multi5;

public class Milk extends Grocery implements Liquid {

	public Milk(String name, String expire) {
		super(name, expire);
	}

}
package oop.multi5;

public class Dish extends Household implements Brokenable {

	public Dish(String name) {
		super(name);
	}

}

 

<기능이 들어있는 Computer만 실행해보기>

package oop.multi5;

public class Test01 {
	public static void main(String[] args) {

		Computer com = new Computer("슈퍼컴퓨터");
		com.on();
		com.off();

	}
}

- 정답

슈퍼컴퓨터 전원이 켜집니다
슈퍼컴퓨터 전원이 꺼집니다

 

Q. 추가 문제

- 각각의 유형별 제품을 담을 수 있는 다음 형태의 클래스를 만들고 객체를 생성해보세요
- ElectronicBox : 전자제품 1개를 보관할 수 있는 상자
- GroceryBox : 식료품 1개를 보관할 수 있는 상자
- HouseholdBox : 생활용품 1개를 보관할 수 있는 상자
- SafeBox : 파손 가능한 상품을 1개 안전하게 보관할 수 있는 상자
- LiquidBox : 액체 상품을 1개 보관할 수 있는 상자
메인에서 각각의 종류별 상자를 만드시고, 상자에 들어갈 제품을 생성해서 상자에 집어넣어 보세요

 

문제가 어렵게 느껴지면 이해하기 쉽게 접근해보자
공통점이 '1개' 보관이다
전자제품말고 정수를 '1개' 보관으로 생각해보면 어떨까?

package oop.multi5;

//정수 1개를 보관할 수 있는 상자
public class IntBox {

	private int value;

	public void setValue(int value) {
		this.value = value;
	}

	public int getValue() {
		return value;
	}

}

-->> 전자제품으로 바꿔보자

package oop.multi5;

//전자제품(Electronic) 1개 저장 클래스
public class ElectronicBox {

	private Electronic value;

	public void setValue(Electronic value) {
		this.value = value;
	}

	public Electronic getValue() {
		return this.value;
	}

}

-->> 나머지도 똑같이 만들어 준다

 

<결과>

업캐스팅이라는걸 알수 수 있다.

package oop.multi5;

public class Test02 {
	public static void main(String[] args) {
		IntBox box1 = new IntBox();
		box1.setValue(100);
		System.out.println(box1.getValue());

		ElectronicBox box3 = new ElectronicBox();
		Computer c = new Computer("슈퍼컴퓨터");
		box3.setValue(c);

		System.out.println(box3.getValue());
		box3.getValue().on();
		box3.getValue().off();
	}
}

 

 

 

 

제네릭(Generic) 클래스 가볍게 알아가기

내가 직접 저장할 형태를 작성해가면서 사용할 수 있는 상자
클래스 옆에다가 <> 를 적고 사이에 이름을 적으면 해당하는 이름을 자료형 처럼 사용 가능
AllInOneBox<E> 라고 적으면 E 라는 미지의 자료형의 데이터를 저장할 수 있는 상자
Generic class(제네릭 클래스)라고 한다

 

all-in-one-box의 사용법

package oop.multi6;

public class AllInOneBox<E> {

	private E value;

	public void setValue(E value) {
		this.value = value;
	}

	public E getValue() {
		return this.value;
	}

}
package oop.multi6;

public class Test01 {
	public static void main(String[] args) {

		// AllInOneBox의 E를 String으로 설정하여 생성
		AllInOneBox<String> box1 = new AllInOneBox<String>();
		box1.setValue("hello");
		System.out.println(box1.getValue());

	}

}

 

 

 

 

 중첩 클래스

일반 중첩 클래스 활용 예시경찰, 강도, 총으로 알아보자

package oop.inner1;

public class Gun {

}
package oop.inner1;

public class Police {
	private Gun gun;

	public Gun getGun() {
		return gun;
	}

	public void setGun(Gun gun) {
		this.gun = gun;
	}
}
package oop.inner1;

public class Robber {
	private Gun gun;

	public Gun getGun() {
		return gun;
	}

	public void setGun(Gun gun) {
		this.gun = gun;
	}
}

--> 이렇게 총을 경찰도 가지고, 강도도 가질 수 있다

만약 권총을 경찰만 소지하도록 하고 싶다면 클래스 구성을 어떻게 해야할까?

대략 다음과 같은 방법들이 있을 것이다.

package oop.inner2;

public class Police { // 외부 클래스
	private Gun gun; 

	public Gun getGun() {
		return gun;
	}

	public void setGun(Gun gun) {
		this.gun = gun;
	}

	// 경찰만이 권총을 사용할 수 있도록
	// = 경찰 클래스 내부에 권총 클래스를 구현
	// = 경찰 외부에서는 Gun이 있느지 알 수 없음
	private class Gun { //내부or 중첩 클래스
		// 클래스이므로 이 안에 구성요소를 모두 가질 것
		// = 멤버 변수, 메소드, 생성자, 중첩클래스
	}

}
package oop.inner2;

public class Robber {
 //아래 코드들은 모두 오류가 발생한다.
//	private Gun gun;
//
//	public Gun getGun() {
//		return gun;
//	}
//
//	public void setGun(Gun gun) {
//		this.gun = gun;
//	}
}

 

 

★★★★★★★★★★

★익명 중첩 클래스 ★

★★★★★★★★★★

package oop.inner3;

public abstract class Button {
	public abstract void push();

}
package oop.inner3;

public class MenuButton extends Button {

	@Override
	public void push() {
		System.out.println("메뉴를 표시합니다");
	}

}
package oop.inner3;

public class ExitButton extends Button {

	@Override
	public void push() {
		System.out.println("프로그램을 종료합니다");
	}

}
package oop.inner3;

public class Test01 {
	public static void main(String[] args) {
		MenuButton button1 = new MenuButton();
		button1.push();

		ExitButton button2 = new ExitButton();
		button2.push();
	}
}

메뉴를 표시합니다
프로그램을 종료합니다

 

--> 단 하나의 객체를 위해 클래스를 만드는게 사치다 - 비효율적

 

익명 중첩 클래스(anonymous inner class)

버튼을 그때그때 상속받아서 서로 다른 기능을 가지도록 구현하자! (일회용개념)
클래스의 몸통만 가져와서 객체 생성시 즉석에서 상속 및 수정하도록 구현하는 방식
일회용 코드를 작성하기 편리하다
상속이 불가능한 클래스(final class)는 생성 불가
추상 클래스, 인터페이스 객체도 생성 가능(상속하므로)

package oop.inner3;

public class Test02 {
	public static void main(String[] args) {
		Button button1 = new Button() {

			@Override
			public void push() {
				System.out.println("메뉴를 표시합니다");
			}
		};
		button1.push();

		// Q : 종료버튼을 button2 라는 이름으로 생성해보세요
		Button button2 = new Button() {

			@Override
			public void push() {
				System.out.println("프로그램을 종료합니다");
			}
		};
		button2.push();
	}
}

메뉴를 표시합니다
프로그램을 종료합니다