Q. 홍만군은 이번 프로젝트의 실무 담당자이다.
그리고 이번 프로젝트에서 필요한 기능 중 일부를 A사에 의뢰할 생각이다.
홍만군이 의뢰한 기능을 요약하면 다음과 같다.
* 이름과 주민등록 번호를 저장하는 기능의 클래스가 필요하다.
* 이 클래스에는 주민등록 번호를 기반으로 사람의 이름을 찾는 기능이 포함되어야 한다.
그리고 이들 기능을 담당하는 메소드는 다음과 같이 정의하고자 하였다.
* 주민등록번호와 이름의 저장 - void addPersonalInfo(String name, String perNum)
* 주민등록번호를 이용한 검색 - String searchName(String perNum)
홍만군이 생각한 해결방법
"클래스를 하나 정의해야겠다. 그리고 A사에는 이 클래스를 상속해서 기능을 완성해 달라고 요구하고,
난 이 클래스를 기준으로 프로젝트를 진행해야겠다."
홍만군이 A사에 의뢰한 내용과 홍만군이 진행한 프로젝트를 합쳐서 하나의 프로그램으로 만드시오.
A. 강사님의 답
abstract class PersonalNumberStorage
{
public abstract void addPersonalInfo(String perNum, String name);
public abstract String searchName(String perNum);
}
class PersonalNumInfo
{
String name;
String number;
PersonalNumInfo(String name, String number)
{
this.name=name;
this.number=number;
}
String getName(){return name;}
String getNumber(){return number;}
}
class PersonalNumberStorageImpl extends PersonalNumberStorage
{
PersonalNumInfo[] perArr;
int numOfPerInfo;
public PersonalNumberStorageImpl(int sz)
{
perArr=new PersonalNumInfo[sz];
numOfPerInfo=0;
}
public void addPersonalInfo(String name, String perNum)
{
perArr[numOfPerInfo]=new PersonalNumInfo(name, perNum);
numOfPerInfo++;
}
public String searchName(String perNum)
{
for(int i=0; i<numOfPerInfo; i++)
{
if(perNum.compareTo(perArr[i].getNumber())==0)
return perArr[i].getName();
}
return null;
}
}
class AbstractInterface
{
public static void main(String[] args)
{
PersonalNumberStorage storage=new PersonalNumberStorageImpl(100);
storage.addPersonalInfo("김기동", "950000-1122333");
storage.addPersonalInfo("장산길", "970000-1122334");
System.out.println(storage.searchName("950000-1122333"));
System.out.println(storage.searchName("970000-1122334"));
}
}
# 인터페이스 : 구현된 것이 없이 밑그림만 있는 것
- 사전적 의미 : 하나의 시스템을 구성하는 2개의 구성 요소(하드웨어, 소프트웨어) 또는
2개의 시스템이 상호작용할 수 있도록 접속되는 경계(boundary),
이 경계에서 상호 접속하기 위한 하드웨어, 소프트웨어, 조건, 규약 등을 포괄적으로 가리키는 말
- abstract 메소드로 이루어져있는 abstract class(추상클래스)는 interface로 바꿀 수 있다.
바꾸기 위해선 interface 안에 있는 모든 메소드는 public abstract으로 선언해야 한다.
- 멤버변수 선언 시 : public static final 멤버변수명
ex)
interface PersonalNumberStrorage
{
void addPersonalInfo(String perNum..)
public void aaa(); // public abstract void aaa();와 같다.
public abstract String ....
int START=1; // public static final int START = 1;와 같음.
- Java는 클래스의 다중상속을 지원하지 않지만,
인터페이스는 인터페이스로부터만 상속 받을 수 있고, 클래스와 달리 다중상속이 가능하다.
ex) 불가예시
class AAA
class BBB
class CCC extends AAA,BBB // (이렇게는 안됨.다중상속)
- 인터페이스를 상속받을때는 extends라고 쓰지 않고, implements라고 써야 한다.
interface AAA
{
void hi();
}
interface BBB extends AAA
{
void bye();
}
interface CCC implements BBB
{
void hi();
void bye(); //hi메소드와 bye메소드를 오버라이딩 해줘야 함.
}
- 인터페이스 내 모든 멤버 변수는 public static final 이어야 하며, 이를 생략할 수 있다.
- 인터페이스 내 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다.
(단, static메서드와 디폴트 메서드는 예외).
- 인터페이스도 자신에 정의된 추상메서드의 몸통을 만들어주는 클래스를 작성해야 함.
(구현해주는 클래스가 해당 인터페이스를 상속받는 것.
대신 용어를 extends가 아닌 구현의 의미로 implements를 사용함.)
ex)
interface AAA
{
public abstract void AAA()
{
}
}
class BBB implement AAA
{
public abstract void AAA()
{
System.out.println("hi");
}
}
- 하기와 같이 상속과 구현을 동시에 할 수도 있음
ex)
class Unit //상위 클래스
{
}
class Fighter extends Unit //상속받는 클래스
{
}
interface Fightable //인터페이스
{
}
class Fighter extends Unit implements Fightable
{
}
// Fighter 클래스가 Unit클래스를 상속받고 Fightable 인터페이스는 구현하는 것이다.
Q. System.out.println 오버로딩 된 메소드 중에 public void println(Object 변수명)가 없다고 가정하고
폴더에 있는 ClassPrinter에 있는 class에 print 메소드를 만드시오.
class ClassPrinter
{
}
class Point
{
private int xPos, yPos;
Point(int x, int y)
{
xPos=x;
yPos=y;
}
public String toString()
{
String posInfo="[x:"+xPos + ", y:"+yPos+"]";
return posInfo;
}
}
class ImplObjectPrintln
{
public static void main(String[] args)
{
Point pos1=new Point(1, 2);
Point pos2=new Point(5, 9);
ClassPrinter.print(pos1);
ClassPrinter.print(pos2);
}
}
A. 강사님의 답
class ClassPrinter
{
public static void print(Object obj)
{
System.out.println(obj.toString());
}
}
class Point
{
private int xPos, yPos;
Point(int x, int y)
{
xPos=x;
yPos=y;
}
public String toString()
{
String posInfo="[x:"+xPos + ", y:"+yPos+"]";
return posInfo;
}
}
class ImplObjectPrintln
{
public static void main(String[] args)
{
Point pos1=new Point(1, 2);
Point pos2=new Point(5, 9);
System.out.println(pos1);
ClassPrinter.print(pos1);
ClassPrinter.print(pos2);
}
}
Q. 위의 문제에서 정의하고 있는 print 메소드에 다음의 기능을 추가하시오.
"인터페이스 UpperCasePrintable을 구현하는 클래스의 인스턴스가
print 메소드의 인자로 전달되면 문자열을 전부 대문자로 출력한다."
A. 강사님의 답
interface UpperCasePrintable
{
// 비어 있음
}
class ClassPrinter
{
public static void print(Object obj)
{
String org=obj.toString();
if(obj instanceof UpperCasePrintable)
{
org=org.toUpperCase();
}
System.out.println(org);
}
}
class PointOne implements UpperCasePrintable
{
private int xPos, yPos;
PointOne(int x, int y)
{
xPos=x;
yPos=y;
}
public String toString()
{
String posInfo="[x pos:"+xPos + ", y pos:"+yPos+"]";
return posInfo;
}
}
class PointTwo
{
private int xPos, yPos;
PointTwo(int x, int y)
{
xPos=x;
yPos=y;
}
public String toString()
{
String posInfo="[x pos:"+xPos + ", y pos:"+yPos+"]";
return posInfo;
}
}
class InterfaceMark
{
public static void main(String[] args)
{
PointOne pos1=new PointOne(1, 1);
PointTwo pos2=new PointTwo(2, 2);
PointOne pos3=new PointOne(3, 3);
PointTwo pos4=new PointTwo(4, 4);
ClassPrinter.print(pos1);
ClassPrinter.print(pos2);
ClassPrinter.print(pos3);
ClassPrinter.print(pos4);
}
}
Q. 인터페이스를 이용해서 상속을 대신하기
폴더에 있는 MultiInheriAlternative.java에서는 다중상속을 어떻게 인터페이스가 대신하는지를 보이고 있다.
이 예제에서 보이는 내용을 참조하여, 이 예제의 IPTV 클래스를 다음의 형태로 변경하자.
class IPTV implements TV, Computer
{
. . .
}
그나마 있던 TV 클래스의 상속도 인터페이스의 구현으로 변경하라는 뜻이다.
물론 예제의 main 메소드에는 변경이 없어야 하며, 실행결과도 동일해야 한다.
class TV
{
public void onTV()
{
System.out.println("영상 출력 중");
}
}
interface Computer
{
public void dataReceive();
}
class ComputerImpl
{
public void dataReceive()
{
System.out.println("영상 데이터 수신 중");
}
}
class IPTV extends TV implements Computer
{
ComputerImpl comp=new ComputerImpl();
public void dataReceive()
{
comp.dataReceive();
}
public void powerOn()
{
dataReceive();
onTV();
}
}
class MultiInheriAlternative
{
public static void main(String[] args)
{
IPTV iptv=new IPTV();
iptv.powerOn();
TV tv=iptv;
Computer comp=iptv;
}
}
A. 강사님의 답
interface TV
{
public void onTV();
}
class TVImpl
{
public void onTV()
{
System.out.println("영상 출력 중");
}
}
interface Computer
{
public void dataReceive();
}
class ComputerImpl
{
public void dataReceive()
{
System.out.println("영상 데이터 수신 중");
}
}
class IPTV implements TV, Computer
{
ComputerImpl comp=new ComputerImpl();
TVImpl tv = new TVImpl();
public void dataReceive()
{
comp.dataReceive();
}
public void onTV()
{
tv.onTV();
}
public void powerOn()
{
dataReceive();
onTV();
}
}
class MultiInheriAlternative
{
public static void main(String[] args)
{
IPTV iptv=new IPTV();
iptv.powerOn();
TV tv=iptv;
Computer comp=iptv;
}
}
Q. 전화번호 관리 프로그램 05단계 문제
우리가 정의한 PhoneBook 클래스는 Manager 클래스로서, 생성되는 인스턴스의 수가 하나인 클래스이다.
이 클래스의 성격을 봐서 알겠지만, 이 클래스의 인스턴스는 둘 이상 생성될 필요가 없으며,
혹시라도 둘 이상의 인스턴스가 생성된다면 이는 실수로 인한 것일 확률이 높다.
그래서 이번 단계에서는 PhoneBook 클래스의 인스턴스 수가 최대 하나를 넘지 않도록 코드를 변경하고자 한다.
그리고 본 프로젝트에서는 프로그램 사용자로부터 다음 중 하나의 선택을 입력 받아서 프로그램을 실행하고 있다.
1. 데이터 입력
2. 데이터 검색
3. 데이터 삭제
4. 모든 데이터 보기
5. 프로그램 종료
뿐만 아니라, 위의 다섯 가지 중에서 '데이터 입력'을 선택하면,
다음 세가지 중 하나의 선택을 추가로 입력 받아서, 그에 맞는 입력의 과정을 진행하고 있다.
1. 일반
2. 대학
3. 회사
한가지 안타까운 점은
이들 메뉴에 대한 정보가 이름이 아닌(이름이 부여된 상수가 아닌) 숫자로 처리되고 있다는 점이다.
때문에 'interface 기반의 상수표현'으로 메뉴의 선택과 그에 따른 처리가,
이름이 부여된 상수를 기반으로 진행되도록 변경하고자 한다.
이를 통해서 우리는 코드의 내용이 보다 명확해진다는 이점을 얻게 될 것이다.
데이터 정보 삭제시 정말 삭제 하시겠습니까? 라는 메세지를 띄우면서 한번 더 물어보기.
A. 강사님의 답
import java.util.Scanner;
class PhoneInfo
{
private String name;
private String phone;
PhoneInfo(String name, String phone)
{
this.name = name;
this.phone = phone;
}
public String getName()
{
return name;
}
public void showPhoneInfo()
{
System.out.println("이름 : " + name);
System.out.println("전화번호 : " + phone);
}
}
class PhoneUnivInfo extends PhoneInfo
{
private String major;
private int year;
PhoneUnivInfo(String name ,String phone, String major, int year)
{
super(name, phone);
this.major = major;
this.year = year;
}
public void showPhoneInfo()
{
super.showPhoneInfo();
System.out.println("전공 : " + major);
System.out.println("학년 : " + year);
}
}
class PhoneCompanyInfo extends PhoneInfo
{
private String company;
PhoneCompanyInfo(String name ,String phone, String company)
{
super(name, phone);
this.company = company;
}
public void showPhoneInfo()
{
super.showPhoneInfo();
System.out.println("회사 : " + company);
}
}
class PhoneBook
{
private static PhoneBook pb;
private PhoneInfo[] pInfo;
private int cntOfPhone;
private int sizePhoneInfo;
PhoneBook(int sizePhoneInfo)
{
pInfo = new PhoneInfo[sizePhoneInfo];
cntOfPhone = 0;
this.sizePhoneInfo = sizePhoneInfo;
}
public static PhoneBook getPhoneBookInst(int sizePhoneInfo)
{
if(pb == null)
pb = new PhoneBook(sizePhoneInfo);
return pb;
}
public void inputPhoneInfo(PhoneInfo pInfo)
{
int i = 0, j=0;
if(cntOfPhone >= sizePhoneInfo)
{
System.out.println("더 이상 저장할 수 없습니다.");
return;
}
for(i=0;i<cntOfPhone;i++)
{
if(this.pInfo[i].getName().compareTo(pInfo.getName()) > 0)
{
for(j=cntOfPhone-1;j>=i;j--)
{
this.pInfo[j+1] = this.pInfo[j];
}
break;
}
}
this.pInfo[i] = pInfo;
cntOfPhone++;
}
public void searchPhoneInfo(String name)
{
int result = search(name);
if(result != -1)
pInfo[result].showPhoneInfo();
else
System.out.println("찾으시는 데이터가 없습니다.");
}
public void deletePhoneInfo(int idx)
{
int i=0;
for(i=idx;i<cntOfPhone-1;i++)
pInfo[i] = pInfo[i+1];
pInfo[i] = null;
cntOfPhone--;
System.out.println("삭제가 완료되었습니다.");
}
public int search(String name)
{
for(int i=0;i<cntOfPhone;i++)
{
if(pInfo[i].getName().compareTo(name) == 0)
return i;
}
return -1;
}
public void showAllPhoneInfo()
{
for(int i=0;i<cntOfPhone;i++)
pInfo[i].showPhoneInfo();
}
}
interface PhoneMenuString
{
int INPUT_PHONEINFO = 1;
int SEARCH_PHONEINFO = 2;
int DELETE_PHONEINFO = 3;
int SHOW_ALL_PHONEINFO = 4;
int PROGRAM_QUIT = 5;
int GENERAL = 1;
int UNIVERCITY = 2;
int COMPANY = 3;
int YES = 1;
int NO = 2;
}
class PhoneUI
{
private static final int MAX_CNT=100;
public static Scanner sc = new Scanner(System.in);
private static PhoneBook pb = PhoneBook.getPhoneBookInst(MAX_CNT);
public static void mainMenu()
{
System.out.println("선택하세요...");
System.out.println("1. 데이터 입력");
System.out.println("2. 데이터 검색");
System.out.println("3. 데이터 삭제");
System.out.println("4. 모든 데이터 보기");
System.out.println("5. 프로그램 종료");
System.out.print("선택 : ");
}
public static void inputMenu()
{
System.out.println("1. 일반, 2. 대학, 3. 회사");
}
public static void inputMenuChoice()
{
int choice=0;
choice = sc.nextInt();
sc.nextLine();
switch(choice)
{
case PhoneMenuString.GENERAL:
inputGeneralPhoneInfo();
break;
case PhoneMenuString.UNIVERCITY:
inputUniversityPhoneInfo();
break;
case PhoneMenuString.COMPANY:
inputCompanyPhoneInfo();
break;
default:
System.out.println("잘못 입력 하셨습니다.");
}
}
public static void inputGeneralPhoneInfo()
{
String name;
String phone;
System.out.println("데이터 입력을 시작합니다.");
System.out.print("이름 : ");
name = sc.nextLine();
System.out.print("전화번호 : ");
phone = sc.nextLine();
System.out.println("데이터 입력이 완료되었습니다.");
pb.inputPhoneInfo( new PhoneInfo(name, phone) );
}
public static void inputUniversityPhoneInfo()
{
String name;
String phone;
String major;
int year;
System.out.println("데이터 입력을 시작합니다.");
System.out.print("이름 : ");
name = sc.nextLine();
System.out.print("전화번호 : ");
phone = sc.nextLine();
System.out.print("전공 : ");
major = sc.nextLine();
System.out.print("학년 : ");
year = sc.nextInt();
sc.nextLine();
System.out.println("데이터 입력이 완료되었습니다.");
pb.inputPhoneInfo( new PhoneUnivInfo(name, phone, major, year) );
}
public static void inputCompanyPhoneInfo()
{
String name;
String phone;
String company;
System.out.println("데이터 입력을 시작합니다.");
System.out.print("이름 : ");
name = sc.nextLine();
System.out.print("전화번호 : ");
phone = sc.nextLine();
System.out.print("회사 : ");
company = sc.nextLine();
System.out.println("데이터 입력이 완료되었습니다.");
pb.inputPhoneInfo( new PhoneCompanyInfo(name, phone, company) );
}
public static void searchPhoneInfo()
{
String name;
System.out.println("데이터 검색을 시작합니다.");
System.out.println("검색하시고자 하는 이름을 입력하세요.");
name = sc.nextLine();
pb.searchPhoneInfo(name);
}
public static void deletePhoneInfo()
{
String name;
int result=0;
int answer=0;
System.out.println("검색하시고자 하는 이름을 입력하세요.");
name = sc.nextLine();
result = pb.search(name);
if(result != -1)
{
System.out.println("정말 삭제하시겠습니까? 1. Yes 2. No");
answer = sc.nextInt();
sc.nextLine();
switch(answer)
{
case PhoneMenuString.YES:
pb.deletePhoneInfo(result);
break;
case PhoneMenuString.NO:
break;
default:
System.out.println("잘못 누르셨습니다.");
}
}
else
System.out.println("삭제하시려는 데이터가 없습니다.");
}
public static void showAllPhoneInfo()
{
pb.showAllPhoneInfo();
}
}
class PhoneMain
{
public static void main(String[] args)
{
int choice=0;
while(true)
{
PhoneUI.mainMenu();
choice = PhoneUI.sc.nextInt();
PhoneUI.sc.nextLine();
switch(choice)
{
case PhoneMenuString.INPUT_PHONEINFO:
PhoneUI.inputMenu();
PhoneUI.inputMenuChoice();
break;
case PhoneMenuString.SEARCH_PHONEINFO:
PhoneUI.searchPhoneInfo();
break;
case PhoneMenuString.DELETE_PHONEINFO:
PhoneUI.deletePhoneInfo();
break;
case PhoneMenuString.SHOW_ALL_PHONEINFO:
PhoneUI.showAllPhoneInfo();
break;
case PhoneMenuString.PROGRAM_QUIT:
return;
default:
System.out.println("잘못 선택하셨습니다.");
}
}
}
}
Q. abstract class PersonalNumberStorage를 인터페이스로 바꿔서 구현하시오.
abstract class PersonalNumberStorage
{
public abstract void addPersonalInfo(String perNum, String name);
public abstract String searchName(String perNum);
}
class PersonalNumInfo
{
String name;
String number;
PersonalNumInfo(String name, String number)
{
this.name=name;
this.number=number;
}
String getName(){return name;}
String getNumber(){return number;}
}
class PersonalNumberStorageImpl extends PersonalNumberStorage
{
PersonalNumInfo[] perArr;
int numOfPerInfo;
public PersonalNumberStorageImpl(int sz)
{
perArr=new PersonalNumInfo[sz];
numOfPerInfo=0;
}
public void addPersonalInfo(String name, String perNum)
{
perArr[numOfPerInfo]=new PersonalNumInfo(name, perNum);
numOfPerInfo++;
}
public String searchName(String perNum)
{
for(int i=0; i<numOfPerInfo; i++)
{
if(perNum.compareTo(perArr[i].getNumber())==0)
return perArr[i].getName();
}
return null;
}
}
class AbstractInterface
{
public static void main(String[] args)
{
PersonalNumberStorage storage=new PersonalNumberStorageImpl(100);
storage.addPersonalInfo("김기동", "950000-1122333");
storage.addPersonalInfo("장산길", "970000-1122334");
System.out.println(storage.searchName("950000-1122333"));
System.out.println(storage.searchName("970000-1122334"));
}
}
A. 내가 생각한 답
interface PersonalNumberStorage
{
void addPersonalInfo(String perNum, String name);
String searchName(String perNum);
}
class PersonalNumInfo
{
String name;
String number;
PersonalNumInfo(String name, String number)
{
this.name=name;
this.number=number;
}
String getName(){return name;}
String getNumber(){return number;}
}
class PersonalNumberStorageImpl implements PersonalNumberStorage
{
PersonalNumInfo[] perArr;
int numOfPerInfo;
public PersonalNumberStorageImpl(int sz)
{
perArr=new PersonalNumInfo[sz];
numOfPerInfo=0;
}
public void addPersonalInfo(String name, String perNum)
{
perArr[numOfPerInfo]=new PersonalNumInfo(name, perNum);
numOfPerInfo++;
}
public String searchName(String perNum)
{
for(int i=0; i<numOfPerInfo; i++)
{
if(perNum.compareTo(perArr[i].getNumber())==0)
return perArr[i].getName();
}
return null;
}
}
class AbstractInterface
{
public static void main(String[] args)
{
PersonalNumberStorageImpl storage=new PersonalNumberStorageImpl(100);
storage.addPersonalInfo("김기동", "950000-1122333");
storage.addPersonalInfo("장산길", "970000-1122334");
System.out.println(storage.searchName("950000-1122333"));
System.out.println(storage.searchName("970000-1122334"));
}
}
//Tip : class 안에 있는 static class는 호칭을 NestedClass라고 함
ㄴ ex) System.out.println의 out의 경우 System 클래스 내에서 Static 클래스로 선언되어 있어
Nested Class로 불리기도 함
- Outer class가 생성되어야 Inner class를 생성할 수 있다.
class Outer //outer와 inner 클래스 간의 연관성이 있는 경우
{
class Inner
{
}
}
ex)
class OuterClass
{
private String myName;
private int num;
OuterClass(String name)
{
myName=name;
num=0;
}
public void whoAreYou()
{
num++;
System.out.println(myName+ " OuterClass "+num);
}
class InnerClass
{
InnerClass()
{
whoAreYou();
}
}
}
class InnerClassTest
{
public static void main(String[] args)
{
OuterClass out1=new OuterClass("First");
OuterClass out2=new OuterClass("Second");
out1.whoAreYou();
out2.whoAreYou();
OuterClass.InnerClass inn1=out1.new InnerClass();
OuterClass.InnerClass inn2=out2.new InnerClass();
OuterClass.InnerClass inn3=out1.new InnerClass();
OuterClass.InnerClass inn4=out1.new InnerClass();
OuterClass.InnerClass inn5=out2.new InnerClass();
}
}
- 메소드 안의 클래스는 로컬 클래스라고 한다.
로컬 클래스는 메소드 안에서만 유효하기 때문에 반환형은 불가능하다.
'SW > Java' 카테고리의 다른 글
[필기정리]Day18 - 전화번호 관리 프로그램 문제 06단계 (0) | 2020.07.03 |
---|---|
[필기정리]Day 17 - 예외사항(try catch), throwable 클래스 (0) | 2020.07.02 |
[필기정리]Day15 - Lotto생성기 및 숫자야구게임 문제 (0) | 2020.06.30 |
[필기정리]Day14 - 전화번호 관리 프로그램 문제 04단계 (0) | 2020.06.29 |
[필기정리]Day13 - final, 상속 (0) | 2020.06.26 |