JavaSE

22일차_추상클래스,인터페이스, 회원관리_배열

알 수 없는 사용자 2015. 6. 21. 07:16

------------------------------------
추상 클래스

추상의 사전적 의미 :
여러 가지 사물이나 개념에서 공통되는 특성이나 속성 따위를 추출하여 파악하는 작용.


1. 추상 클래스는 추상 메소드를 포함하고 있는 클래스이다. 단, 추상 메소드가 없어도 추상 클래스로 선언할 수 있다. 추상 클래스로 지정되면 객체 생성을 할 수 없다.

2. 추상 메소드는 메소드 정의만 있고, 구현이 없는 메소드이다. 메소드 구현을 하위 클래스에서 의무적으로 하도록 하기 위함이다. 추상 메소드는 현재 클래스를 상속받는 클래스가 공통적으로 사용하게 될 메소드를 정의만 하고, 실제 구현은 하위 클래스에서 하도록 만든 것이다.

3. 추상 메소드 기본 형식
접근지정자 abstract 반환자료형 메소드이름(매개변수리스트);

4. 추상 클래스 기본 형식
public abstract class 클래스이름 {
 //추상 메소드 멤버
}

 

예를 들어, 프린터 클래스가 있다면, 인쇄 기능은 모든 프린터의 공통 기능이지만 프린터마다의 방식이 다르므로 하위 클래스에서 구현하도록 하는 것이다.

public abstract class 프린터 {
 public abstract void 인쇄();
}

public class 잉크젯프린터 extends 프린터 {
 @Override
 public void 인쇄() {
  //잉크젯 방식 인쇄 실행
 }
}

public class 레이져프린터 extends 프린터 {
 @Override
 public void 인쇄() {
  //레이저 방식 인쇄 실행
 }
}

 

 


//Printer.java
package com.test;

//프린터 클래스들에 대한 공통적 요소를 모은 클래스
//-> 추상 클래스 정의
//-> 자기 자신은 객체 생성 불가
public abstract class Printer {
 
 //일반 메소드 형식
 /*
 public void print() {
  //기능 구현 X
 }
 */
 
 //추상 메소드 형식
 //-> 기능 구현은 없고, 메소드 이름만 존재하는 상태
 //-> 모든 프린터 클래스가 가져야될 공통 기능.
 //-> 추상 메소드 추가
 //-> 추상 메소드가 있는 클래스는 추상 클래스로 지정.
 public abstract void print();
 
 //일반 메소드 추가
 //추상 클래스는 객체 생성이 안되므로 직접 호출 불가
 //->하위 클래스의 객체를 통해서 호출 가능
 public int count() {
  int result = 0;
  //출력 매수를 계산하는 과정
  return result;
 }

}

 

 

 

 


//InkJetPrinter.java
package com.test;

//상위 클래스가 추상 클래스인 경우는
//추상 클래스의 추상 메소드를 반드시 오버라이딩 구현.
public class InkjetPrinter extends Printer {

 //의무적 오버라이딩
 @Override
 public void print() {
  //잉크젯 프린터의 인쇄 기능 구현
 }

 //선택적 오버라이딩
 @Override
 public String toString() {
  return "InkjetPrinter 클래스의 toString() 메소드";
 } 
 

}

 

 


//LaserPrinter.java
package com.test;

public class LaserPrinter extends Printer {

 //추상 클래스를 상속 받은 하위 클래스는
 //상위 클래스의 추상 메소드 멤버를 의무적으로 오버라이딩해야 한다.
 //메소드 내부에서 구체적인 구현 내용을 채워야 합니다.
 @Override
 public void print() {
  System.out.println("레이저 방식으로 인쇄 실행!");
 }
 
}

 

 


//Main.java
package com.test;

public class Main {

 public static void main(String[] args) {
  
  //추상 클래스는 객체 생성 불가
  //Printer printer = new Printer(); //X
  //printer.count(); //X
  //printer.print(); //X
  
  //하위 클래스는 객체 생성 가능
  InkjetPrinter printer = new InkjetPrinter();
  //Printer 클래스의 count() 메소드를
  //하위 클래스인 InkjetPrinter 클래스의
  //멤버로 인식.
  System.out.println(printer.count());
  printer.print();
  

 }

}

 

 

Wrapper 클래스의 상위 클래스인 Number 클래스(추상 클래스) 테스트
//Main.java
package com.test;

public class Main {

 public static void main(String[] args) {

  //Number 클래스는 Wrapper 클래스의 상위 클래스입니다.
  //Number 클래스는 추상 클래스입니다.
  //-> 추상 메소드 포함된 상태
  //-> 객체 생성 불가
  //-> 상속한 하위 클래스를 통해서 멤버 접근 가능
  
  //추상 클래스이므로 객체 생성이 불가합니다.
  //Number number1 = new Number();
  
  //Integer 클래스는 기본생성자가 없기 때문에
  //매개변수 있는 생성자를 통해서 객체 생성 해야 합니다.
  //Number number1 = new Integer();
  Number number1 = new Integer(10);
  System.out.println(number1.intValue()); //10
  System.out.println(number1.doubleValue()); //10.0
  
  //Number 클래스에서의 doubleValue() 메소드
  /*
  public abstract double doubleValue();
  */
  
  //Integer 클래스에서의 doubleValue() 메소드
  /*
  public double doubleValue() {
   return (double)value;
  }
  */
  
 }

}

 

 

--------------------------------------
인터페이스(Interface)

1. 추상 메소드와 상수만을 멤버로 가질 수 있는 추상 클래스. 모든 멤버가 추상이므로 abstract 키워드를 사용하지 않는다. 추상 클래스이므로 객체 생성이 안된다. 인터페이스 이름은 일반적으로 ~able 형태로 작성한다.

2. 인터페이스 기본 형식

public interface 인터페이스명 {
 //상수 멤버
 //추상 메소드 멤버
}

3. 모든 멤버 변수는 public static final 인 상태이다. 생략 가능.

4. 모든 메소드 멤버는  public abstract 인 상태이다. 생략 가능.

5. 인터페이스는 인터페이스만 상속 받을 수 있고, 다중 상속 받을 수 있다.

public interface 하위인터페이스 extends 상위인터페이스1, 상위인터페이스2, ... {
}


6. 클래스는 상위 클래스를 한 개만 상속 받을 수 있지만, 인터페이스는 다중 상속 받을 수 있다.

public class 하위클래스 extends 상위클래스 implements 상위인터페이스1, 상위인터페이스2,... {
}

7. 인터페이스를 상속 받은 클래스는 인터페이스가 가진 멤버를 의무적으로 구현(implement)해야 한다.


8. 인터페이스는 클래스의 추상화에 필수적인 조건이다. 서로 관계 없는 클래스들끼리도 관계를 맺어줄 수 있다.

예를 들어,

A -> 커피숍 주인. 원두 커피 사용. B에게서 공급 받고 있다.
B -> 원두 커피 공급업자

=> A 와 B은 강한 결합 관계가 된다.
=> 강한 결합이기 때문에 B의 상황과 능력이 A에게 영향을 준다.
=> 약한 결과 관계를 만들기 위해서 중간 역할자를 부여한다.

A -> 커피숍 주인. 원두 커피 사용. C에게서 공급 받고 있다.
C -> 중간 역할자(도매상)
B1, B2,... -> 원두 커피 공급업자
=> 약한 결합이기 때문에 B1의 상황과 능력이 A에게 직접적으로 영향을 주지 않는다.
 B1을 대체할 수 있는 방법이 많이 존재하기 때문이다.

 

A 객체-> B1 객체를 사용해야 하는 상황. -> B1 객체를 직접 사용하지 않고, ( 인터페이스 ) 를 통해서 사용하도록 조정. -> B1 객체 대신 B2, B3 객체로 바꿔서 사용할 수 있다.

인터페이스 -> 중간 역할. B1, B2, B3 객체의 추상 클래스.

B1 객체 -> 대체 객체로서 B2, B3, ... 등이 존재.

 

--------------------------------------------
추상화

- 클래스간의 결합에서 A와 B가 직접적인 연결을 하는 것이 아니라, 인터페이스를 통해서 연결함으로써 A와 B의 연결을 간접적으로 연결하고, B의 상태가 변화(C로 바뀌는 경우) 하더라도 A에는 영향을 최소화 할 수 있는 상태를 만드는 것.


추상화 전 상태
//Sample35.java
package com.test;

public class Sample35 {
 
 private Sample36 sample;
 //Sample36에서 Sample37로 대체하려면
 //-> private Sample37 sample;
 
 //Sample35 객체의 생성자에서
 //Sample36 클래스의 객체 생성 과정이 포함되어 있다.
 //Sample35와 Sample36은 강한 결합 상태이다.
 public Sample35() {
  sample = new Sample36();
  //Sample36에서 Sample37로 대체하려면
  //-> sample = new Sample37();
 }
 
 public String method() {
  return sample.toString();
 }

}

 


//Sample36.java
package com.test;

public class Sample36 {

 @Override
 public String toString() {
  return String.format("Sample36 클래스의 toString() 메소드");
 }

}

 


//Sample37.java
package com.test;

//Sample36 클래스의 대체 클래스 예정
public class Sample37 {

 @Override
 public String toString() {
  return String.format("Sample37 클래스의 toString() 메소드");
 }

}

 


//Main.java
package com.test;

public class Main {

 public static void main(String[] args) {
  
  //사용자는 Sample35 객체만 생성했지만
  //내부적으로는 Sample36 객체도 생성된다.
  Sample35 sample = new Sample35();
  System.out.println(sample.method()); //"Sample36 클래스의 toString() 메소드"

 }

}

 


추상화 후 상태 (인터페이스 추가)
//Sample.java
package com.test;

public interface Sample {
 
 void method();

}

 


//Sample35.java
package com.test;

public class Sample35 {
 
 private Sample sample;
 //Sample36에서 Sample37로 대체하려면
 //->수정 사항 없음
 
 //Sample35 객체의 생성자에서
 //외부에서 Sample36, Sample37 클래스의 객체를 얻어오는 과정이 포함되어 있다.
 //Sample35와 Sample36, Sample37은 약한 결합 상태이다.
 public Sample35(Sample sample) {
  this.sample = sample;
  //Sample36에서 Sample37로 대체하려면
  //-> 수정 사항 없음
 }
 
 public String method() {
  return sample.toString();
 }

}

 

 

//Sample36.java
package com.test;

public class Sample36 implements Sample {

 @Override
 public String toString() {
  return String.format("Sample36 클래스의 toString() 메소드");
 }

 @Override
 public void method() {
 }

}

 

 

//Sample37.java
package com.test;

//Sample36 클래스의 대체 클래스 예정
public class Sample37 implements Sample {

 @Override
 public String toString() {
  return String.format("Sample37 클래스의 toString() 메소드");
 }

 @Override
 public void method() {
 }

}

 

 

//Main.java
package com.test;

public class Main {

 public static void main(String[] args) {

  //Sample36 객체는 별도 생성합니다.
  Sample36 sample36 = new Sample36();
  //Sample37 객체는 별도 생성합니다.
  Sample37 sample37 = new Sample37();
  
  //사용자는 Sample35 객체를 생성하는 과정에서
  //Sample36 객체를 주입하는 과정이 실행 된다.
  //Sample36에서 Sample37로 대체하려면
  //생성자 매개변수 값으로 sample36 -> sample37로 수정하면 된다.
  Sample35 sample = new Sample35(sample37);
  System.out.println(sample.method()); //"Sample36 클래스의 toString() 메소드"

 }

}

 

-------------------------------------------------
과제)외부에서 데이터(이름, 전화번호)를 입력 받아서 메모리에 저장하고 출력, 검색할 수 있는 회원 입력, 출력 프로그램 작성.
회원 입력시 배열 저장소의 공간이 모자란 경우 배열 저장소의 크기를 확장(2배)하는 기능 추가.

실행 예)
---- 회원관리 ----
1. 회원 전체 출력
2. 회원 입력
3. 회원 검색
선택(1~3, 0 종료)?

//Student.java -> 회원 한 명분의 정보 저장용 클래스. 이름, 전화번호 항목으로 구성.
//MenuAction.java -> 메뉴 액션 클래스. 최초 입력시 회원 n명까지 저장할 수 있는 배열 생성. 배열의 자료형은 Student 클래스로 지정.
//Main.java -> 메뉴 출력

 

//Student.java
package com.sist;

public class Student {
 
 private String name, tel;

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

 public String getTel() {
  return tel;
 }

 public void setTel(String tel) {
  this.tel = tel;
 }


 //toString() 메소드 오버라이딩 구현
 //멤버변수의 데이터를 출력하는 용도로 변경
 @Override
 public String toString() {
  
  return String.format("%s %s"
    , this.getName()
    , this.getTel());
  
 }

 //문제) equals() 메소드 오버라이딩
 //equals()는 Object 클래스의 메소드
 //equals()는 원래 객체의 참조주소를 비교하는 기능이지만
 //객체의 멤버(name)을 비교하도록 변경
 @Override
 public boolean equals(Object obj) {

  //원본 -> this.name
  //비교 대상 -> obj.getName(); //X
  //비교 대상 -> ((Student)obj).getName(); //O
  //비교 방법 -> 같은지, 다른지 ? -> true or false -> 비교 대상의 자료형 -> String -> equals() 메소드 -> 문자열 비교
  return this.name.equals(((Student)obj).getName());
   
 }
 
 

}

 

 


//MenuAction.java
package com.sist;

import java.util.Scanner;

public class MenuAction {
 
 //학생 정보 저장용 배열 준비->데이터 저장소
 private Student[] students;
 
 public MenuAction() {
  //배열 생성 및 초기화
  students = new Student[5];
  this.init();
 }
 
 private void init() {
  
  Student student0 = new Student();
  student0.setName("hong");
  student0.setTel("010-123-1234");
  students[0] = student0;
  
  Student student1 = new Student();
  student1.setName("kim");
  student1.setTel("010-432-4321");
  students[1] = student1;

  Student student2 = new Student();
  student2.setName("park");
  student2.setTel("010-987-9876");
  students[2] = student2;
 
  Student student3 = new Student();
  student3.setName("choi");
  student3.setTel("010-567-5678");
  students[3] = student3;
 
  Student student4 = new Student();
  student4.setName("hwang");
  student4.setTel("010-876-8765");
  students[4] = student4;

 }
 
 
 //1. 회원 입력
 public void menuInsert(Scanner sc) {
  //문제) 신규 정보 등록 과정 추가
  
  //배열 크기 및 여분 확인 -> 입력 가능 여부 결정
  //입력-> 새로운 name, tel
  //처리-> 저장소(배열)에 신규 저장
  //출력-> "신규 데이터 입력!" or "저장소 full!" 메시지 출력
  if (students.length > this.count()) {
   System.out.print("이름 입력 : ");
   String name = sc.next();
   System.out.print("전화번호 입력 : ");
   String tel = sc.next();
   Student stu = new Student();
   stu.setName(name);
   stu.setTel(tel);
   students[this.count()] = stu;
   System.out.printf("신규 데이터 입력 완료! %n%n");
  } else {
   System.out.printf("저장소 Full! %n");
   
   //확장 여부 확인
   System.out.print("저장소를 확장하시겠습니까(y/n)?");
   String response = sc.next();
   if (response.equalsIgnoreCase("Y")) {

    //문제) 확장 액션 진행
    //-> private 메소드
    this.expand();
    
    System.out.printf("저장소가 확장되었습니다.%n%n");
   }
   
  }  
  
  
 }
 
 //저장소 확장 액션 메소드
 private void expand() {

  //신규 임시 배열 준비
  //-> 크기는 기존 배열의 2배 또는 5칸 증가
  //기존 배열의 데이터를 임시 배열로 복사 -> 반복문
  //신규 임시 배열의 참조주소를
  //기존 배열 참조주소를 저장한 변수(students)에 덮어쓰기.
  
 
 }
 
 //2. 회원 전체 출력
 public void menuSelect() {
  
  //출력 -> 저장소(배열)에 있는 모든 데이터
  /*
  for (int i=0; i<students.length; ++i) {
   Student student = students[i];
   if (student != null) {
    //System.out.println(student.toString());
    System.out.println(student);
   }
  }
  */

  System.out.printf("인원수:%d %n", this.count());
  System.out.println("---------------");
  System.out.println("이름 전화번호");
  System.out.println("---------------");
  for (Student student : students) {
   if (student != null) {
    System.out.println(student);
   }
  }
  System.out.println("---------------");
  System.out.println();
 }
 
 
 //인원수 메소드 -> private
 private int count() {
  int result = 0;
  
  //문제) 저장소(배열)에 들어있는 데이터의 갯수 확인
  for (int i=0; i<students.length; ++i) {
   Student student = students[i];
   if (student != null) {
    ++result;
   }
  }  
  
  return result;
 }
 
 
 //3. 회원 검색
 public void menuSearch(Scanner sc) {
  //외부 입력(name)
  System.out.print("name?");
  String name = sc.next();
  
  Student value = new Student();
  value.setName(name);
  
  //처리 -> 검색 액션
  //-> 배열 저장소 내에서 name이 일치하는 자료 검색
  //-> Student 자료형을 가진 객체 한 개
  Student result = null;
  for (int i=0; i<students.length; ++i) {
   Student temp = students[i];
   /*
   if (temp.getName().equals(name)) {
    result = temp;
    break;
   }
   */

   if (value.equals(temp)) {
    result = temp;
    break;
   }
   
  }
  
  //결과 출력
  if (result == null) {
   System.out.printf("검색 결과가 없습니다.%n%n");
  } else {
   /*
   System.out.printf("%s %s %n%n"
     , result.getName()
     , result.getTel());
   */
   
   /*
   System.out.println(result); //객체 정보
   System.out.println(result.toString()); //객체 정보
   */
   
   //toString() 메소드 호출시 객체 정보가 아니라
   //멤버변수의 데이터를 출력하는 용도로 변경
   //->오버라이딩
   System.out.println(result);
   System.out.println();
   
  }
 }

}

 

 

//Main.java
package com.sist;

import java.util.*;

public class Main {

 public static void main(String[] args) {
  
  Scanner sc = new Scanner(System.in);
  MenuAction menu = new MenuAction();
  
  do {
   System.out.println("--- 회원 정보 관리 ---");
   System.out.println("1. 회원 전체 출력");
   System.out.println("2. 회원 입력");
   System.out.println("3. 회원 검색");
   System.out.print("선택(1, 0-exit)?");
   int m = sc.nextInt();
   if (m==0) {
    break;
   }
   switch (m) {
   case 1: menu.menuSelect(); break;
   case 2: menu.menuInsert(sc); break;
   case 3: menu.menuSearch(sc); break;
   }
   
  }while(true);
  
  sc.close();

 }

}

 

 


------------------------------------------
요약

1. 추상 클래스의 특징
- 추상 메소드가 포함된 클래스
- 추상 메소드 : 메소드 정의만 있고, 구현 내용이 없는 메소드.
- abstract 키워드 사용
- 추상 클래스는 객체 생성 안된다.
- 추상 클래스에 일반 멤버(생성자, 멤버변수, 메소드) 포함할 수 있다.
- 추상 클래스를 상속 받은 하위 클래스는 반드시 추상 메소드에 대한 오버라이딩을 구현해야 한다.


2. 인터페이스
- 클래스의 추상화
- 인터페이스를 이용하면 클래스간의 관계를 약한 결합으로 만들 수 있다.
- 인터페이스는 모든 멤버가 추상이므로, abstract 키워드를 생략한다.
public interface 인터페이스명 {
 //추상 멤버
}

---------------------------------------------------