。゚(*´□`)゚。

코딩의 즐거움과 도전, 그리고 일상의 소소한 순간들이 어우러진 블로그

[네이버클라우드] 클라우드 기반의 개발자 과정 7기/웹프로그래밍

[NC7기-49일차(7월4일)] - 웹프로그래밍 30일차

quarrrter 2023. 7. 4. 15:06

oop ex07 

추상클래스

클래스 앞에 abstract 붙이기.

목적: 서브 클래스에게 공통 필드나 메서드 등을 상속해주는 역할, 여러 클래스를 같은 타입으로 묶을 때. 

추상클래스는 인스턴스를 생성할 수 없다. 레퍼런스는 생성할 수 있다.

A를 상속받아 레퍼런스 생성가능.

 

추상클래스의 추상메서드

메서드 선언부에 abstract 붙이기.

메서드 바디가 없다 { }

추상클래스나 인터페이스에서만 선언 가능. 

서브 클래스마다 구현이 다를 수 있는 경우에 사용.

서브클래스에서 상속 받아서 Override 해야함 ~

package com.eomcs.oop.ex07.a;

abstract class A3 {
  public abstract void m1();
}

class A3Sub extends A3 {
  @Override // 이 애노테이션은 빼도 된다.
  public void m1() {
    System.out.println("A3Sub.m1() 호출됨!");
  }

  public void m2() {
    System.out.println("A3Sub.m2() 호출됨!");
  }
}

public class Exam03 {
  public static void main(String[] args) {
    A3 obj;

    // 추상 클래스의 인스턴스는 생성 불가!
    //    obj = new A3(); // Error!

    // 추상 메서드를 구현한 서브 클래스 만이 인스턴스 생성 가능!
    obj = new A3Sub();

    // 오버라이딩 규칙에 따라
    // - 레퍼런스가 실제 가리키는 객체의 클래스에서부터 메서드를 찾는다.
    obj.m1();   --obj에 실제 저장된건 A3Sub이기 때문에 A3Sub에서 m1을 찾는것임. 추상메서드 호출이아님

    // 참고!
    //
    // - 레퍼런스가 실제 가리키는 객체가 A3Sub 라 하더라도
    //   레퍼런스 타입의 범위를 넘어서 메서드를 호출할 수는 없다.
    //    obj.m2(); // 컴파일 오류!

    // - 물론 실제 인스턴스 타입으로 형변환 후에는 가능한다.
    ((A3Sub)obj).m2();
  }

  static void test(A3 obj) {
    obj.m1();
    // 엥? 
    // obj는 A3의 레퍼런스이다.
    // A3의 m1() 은 추상 메서드이다.
    // 그런데 어떻게 m1() 을 호출하는가?
    //
    // 답변:
    (A3 obj) : A3서브 클래스를 받겠다 
    // - A3는 추상 클래스이기 때문에 A3의 인스턴스는 생성할 수 없다.
    // - 따라서 obj 파라미터에 넘어오는 인스턴스(주소)는 
    //   A3의 인스턴스가 아니라 A3의 자식 클래스의 인스턴스일 것이다.
    // - 인스턴스를 만들었다는 의미는 A3의 자식 클래스로서 
    //   A3의 모든 추상 메서드를 구현했다는 의미다.
    // - 따라서 obj를 가지고 m1() 를 호출할 때는 
    //   실제 obj가 가리키는 인스턴스의 클래스에서 m1() 메서드를 찾아 호출한다.
    // - 다시 한 번 말하지만 인스턴스를 만들었다는 것은 
    //   일반 클래스라는 뜻이고,
    //   일반 클래스에는 추상 메서드가 없다.
    //   그러므로 A3를 상속 받은 일반 클래스는 반드시 m1() 메서드를 구현했다는 의미다.
    // 


  }

}

GoF의 Template Method 패턴

알고리즘(실행흐름, 전략)의 뼈대를 정의. 

=> 수퍼 클래스에서 틀을 잡고 세세한 동작의 구현은 서브클래스에게 맡긴다. 

 

1.알고리즘짜기

정렬, 검색, 탐색, 디지털지문 생성하는 방법 : 모두 알고리즘

2. Template Method

추상메서드를 만들고, 세세한 구현은 서브클래스에 맡기기 

=> 보고서 출력에 대한 틀(template)만 잡고 실제 출력은 서브클래스에서 결정한다. 

 

3. <<abstract>> Letter를 상속 받은 LoveLetter가 템플릿메서드를 구현.

4. <<abstract>> Letter를 상속 받은 ReportLetter가 템플릿메서드를 구현.

 


*추상클래스 사용 전 

BubbleSort - run {}

QuickSort - start {} 

.

두 객체의 사용법이 다르다. 

일관성이 부족하고, 유사코드를 중복해서 작성해야하며, 유지보수 및 개발비가 올라간다. ,,,, 

 

*추상클래스 사용 후 - Generalization 수행

sorter라는 수퍼클래스를 만들어서 자식으로 버블이랑 퀵 두기 

sort() {} <- 빈 상태로 두기. 어차피 서브클래스가 재정의 => 문제점: sorter클래스를 직접 사용할 것도 아닌데 일반클래스로 만드는 것은 바람직하지 않다. sorter를 abstract로 추상클래스로 만들긔 ~ 

BubbleSort - sort {} 이름 변경 

QuickSort - sort {} 추가로 만들어서 start 호출 

=> 버블을 쓰나 퀵을 쓰나 정렬객체sort ()의 사용법이 같다. 

 

*추상 메서드 적용

sort() { } 를 abstract 추상메서드로 만들어서 서브클래스에서 강제 Overriding하게 만들기. 

 

* 구현 메서드가 없다면 호출 규칙을 정의하는 인터페이스로 만드는 것이 낫다 ..! 

- 모든 메서드는 기본이 public이구 abstract다. 따라서 메서드 선언에 생략해도 된다. 

sorter를 인터페이스로 만들기. 

 


ex08 자습

캡슐화(encapsulation) :

자바는 필드나 메서드의 외부 접근 범위를 조정하는 문법.

 

게터(getter) //  get필드명() {...}

필드의 값을 조회하는 용도로 사용하기 위해 메서드를 만들 경우
 메서드의 용도를 이해하기 쉽도록 공개 모드로 getXxx() 형태로 이름을 짓는다.

세터(setter) set필드명() {...}
필드의 값을 설정하는 메서드. 외부에서 인스턴스 변수에 접근을 못하기 때문에 값을 넣거나 조회할 수 없기에 이를 가능하게 하는 수단/방법(method)을 제공해야 한다.
직접 필드의 값을 바꾸게 하지 말고 메서드를 통해 바꾸도록 유도
외부에서 호출할 수 있도록 공개 모드로 설정한다.


# 객체지향 프로그래밍(Object-Oriented Programming; OOP)의 특징

1) 추상화(abstraction)

- 프로그램에서 다루는 데이터나 코드를 클래스로 정의하는 행위.

- 클래스 멤버(스태틱 멤버) : 스태틱 필드, 스태틱 블록, 스태틱 메서드

- 인스턴스 멤버 : 인스턴스 필드, 인스턴스 블록, 인스턴스 메서드, 생성자

 

2) 상속(inheritance)

- 기능 확장을 위한 문법

 

3) 캡슐화(encapsulation)

- 외부의 접근을 제어하는 문법

 

4) 다형성(polymorphism)

- 하나의 코드가 여러 용도로 쓰이게 하는 것.

- 오버로딩(overloading)

- 메서드 시그너처가 다르더라도 같은 기능을 하는 메서드에 대해 같은 이름을 갖게하여

일관성 있는 프로그래밍을 하게 도와주는 문법.

- 오버라이딩(overriding)

- 상속 받은 메서드를 자신의 역할에 맞게 재정의 하는 .

- 다형적 변수(polymorphic variable)

- 하나의 변수가 여러 타입을 가리킬 수 있고, 다양한 타입으로 다뤄질 수 있게 도와주는 문법.

 

# 캡슐화

- 클래스 멤버나 인스턴스 멤버의 접근을 제한하는 문법이다.

- 이유? 잘못된 사용으로 결과가 왜곡되는 것을 막기 위함이다.

- 정의된 대로 역할을 수행하게 도와준다.

- 문법: 클래스 멤버나 인스턴스 멤버 선언할 접근 제한자(modifier)를 붙인다.

- 접근 제한자

- private : 클래스에 소속된 같은 멤버만 접근 가능

- (default) : 같은 패키지에 소속된 멤버만 접근 가능

- protected : 같은 패키지에 소속되거나 자손 클래스의 멤버만 접근 가능

- public : 모두 접근 가능

 

# getter/setter

- 캡슐화와 더불어 사용되는 기술이다.

- 필드에 대해 외부의 직접적인 접근을 막는 대신 메서드를 통해 값을 변경, 조회하도록 유도한다.

- 메서드에서 값의 유효 범위를 검사하여 변경을 허락할 수 있다.

- getter/setter 를 다른 말로 "프로퍼티(property)"라 부른다.

- getter만 있는 경우: read only 프로퍼티

- setter만 있는 경우: write only 프로퍼티

- getter/setter 모두 있는 경우: read/write 프로퍼티


client 

네트워킹: 요청하는 s/w

oop: 객체를 사용하는 쪽 


인터페이스:  caller와 callee사이의 호출 규칙

호출 규칙: 메서드 형식 , 몸체는 정의하지 않는다. 

public 외 다른 공개범위는 허용될 수 없다. 왜냐묜 규칙이니까 전체공개 

 

// 참고!
Worker w;


// 인터페이스 레퍼런스?
// => 해당 인터페이스에 따라 작성된(사용규칙을 준수하는) 클래스의 인스턴스 주소를 저장한다.
//
// 위 예제에서 w 레퍼런스를말로 표현하는 방법:
// => Worker 사용규칙에 따라 작성된 클래스의 인스턴스 주소를 저장하는 변수 w.
// => Worker 인터페이스를 구현한 클래스의 인스턴스 주소를 저장하는 변수 w.
// => Worker 구현체의 인스턴스 주소를 저장하는 변수 w.
// => Worker 구현체의 객체 주소를 저장하는 변수 w.
// => Worker 구현 객체를 저장하는 변수 w.
// => Worker 객체를 저장하는 (변수) w.
// => Worker 객체를 가리키는 (변수) w.
// => Worker 타입 객체 w.

공식적으로 인터페이스 문법을 따르지 않은 클래스는 w에 올 수 없다. 

 

public, abstract 생략 가능 
private, protected, (default)는 없다.
인터페이스 필드는 public static final 이다.
- 인스턴스를 생성할 수 없기 때문에 인스턴스 필드를 선언할 수 없다.
- 인스턴스 필드가 아니기 때문에 값을 변경할 수 없다.
  public static final int v1 = 100;

 public, static, final 을 생략할 수 있다.
int v5 = 500;
default method
새 메서드는 새 프로젝트의 구현체가 오버라이딩 할 것이니  자세하게 정의하지 않는다.
다만 이 인터페이스를 구현한 예전 프로젝트에 영향을 끼치지 않으면서 새 메서드를 정의할 때 사용하는 문법이다.
추상메서드는 반드시 override하기, default는 override해도 되고 안 해도 되고.
인터페이스 내부에서 사용할 구현 메서드라면 private 으로 해도 된다. 
터페이스도 클래스처럼 static 메서드를 정의할 수 있다.
인터페이스는 super클래스로 취급하지 않는다. 
서브클래스의 super클래스는 Object임~ 
인터페이스의 메서드를 call하고 싶으면..
A.super.m2();  // A 인터페이스의 오버라이딩 전 m2를 호출해다오 
A.m2(); 컴파일 오류 : 인스턴스주소 없이 A의 m2호출 불가 
인터페이스에 정의된 static 메서드는,
 => 인터페이스를 구현한 클래스를 통해 호출할 수 없다.
  MyInterface5Impl.m1(); // 컴파일 오류!
   인터페이스도 다른 인터페이스를 상속 받을 수 있다.
  수퍼 클래스와, 수퍼 인터페이스 모두 구현해야한다. 
컴파일 단계에서는 변수의 타입이 뭔지만 따짐.  컴파일 단계에서는 변수의 타입에 정의된 메서드들만 호출 허용한다.  
인터페이스 다중 상속 가능. => 인터페이스의 메서드는 추상메서드이기 때문에 중복되어도 상관없다. 
인터페이스 다중 구현 가능.
다중 상속 불가능한 경우도 있음
추상메서드들의 리턴타입이 다를 때.. ex) A의 m1은 void이고, B의 m1이 int일 때. 
메서드 시그니처(이름, 파라미터)는 같고 리턴타입이 다른 메서드를 중복으로 정의할 수 없다. => 오버로딩 불가 => 다중구현도 역시 불가

다중 인터페이스를 구현이 불가한 경우,
  // - 메서드 이름만 같고
  //   메서드 시그너처의 다른 부분(파라미터, 리턴타입)이 다른 경우.
  // - 두 인터페이스를 모두 만족시키지 못하기 때문이다.

메서드 시그니처가 다르면 다중구현 가능. 
추상 메서드인 경우 구현하지 않았기 때문에 다중 구현시 중복되더라도 문제가 되지 않는다. 
구현한 메서드인 경우 중복 다중 구현시 중복되면 문제 발생하기 때문에 컴파일 오류 
 

*참고 

인터페이스 구현 클래스의 이름 짓는 예 

<<interface>> Car

- DefaultCar //기본 구현체라는 의미 

-CarImpl //Car 구현체임을 표시

 

*인터페이스와 추상클래스

1. 인터페이스 직접 구현 

<<interface>> A  .m1()   .m2()

AImpl m1(){..} m2(){...}

 

2. 추상클래스를 통한 간접 구현 

추상클래스의 역할 : 인터페이스를 구현하기 쉽도록 미리 메서드를 간단하게 overriding 

필요한 메서드만 overriding 하기 때문에 인터페이스 구현이 간결해짐 

다른 데서 많이 쓰이는 것만 추상메서드로 구현하고 concrete 클래스에서 남은거 구현 

 

*인터페이스와 추상클래스 - Java API

<<interface>>
Collection
add()
contains()
isEmpty()
remove()
size()
toArray()
목록을 다루는 규칙을 정의
(collection)
Collection 상속 받음 
<<interface>>
List
get()
indexOf()
of()
Set()
subList()
인덱스를 사용하여 항목을 찾고 변경하고 삭제, 변하는 방법을 추가
Collection을 구현한 
<<abstract>>
AbstractCollection 
abstract iterator()
데이터를 조회하는 방법이 서브클래스에 따라 다를 수 있기 때문에 추상메서드로 냅둔다. 
(즉 이 클래스에서 정의하지 않는다.)
 
List를 구현하고
AbstractCollection 를 상속 받음 
<<abstract>>
AbstractList
iterator() { ...}  // 일부 추상메서드 구현
<<abstract>> get() 은 구현하지 않음 
-> 서브 클래스마다 데이터를 꺼내는 방식이 다를 수 있기 때문에 구현하지 않음
AbstractList를 상속받은
<<concrete>>
ArrayList
get(){...} : 배열에서 값을 꺼냄   
AbstractList를 상속받은
<<concrete>>
LinkedList
get(){...} : 노드 체인에서 값을 꺼냄.  

 


디폴트 메서드(default method)


구현할 코드 있으면 작성하고, 없으면 빈 채로 둔다.
디폴트 메서드로 새 규칙을 추가하면 기존 클래스 모두 컴파일 오류가 발생하지 않는다.
=> 디폴트 메서드는 구현된 메서드이기 때문이다.
  
가능한 일반 클래스의 메서드처럼 사용하지 말아라!
새 규칙을 추가하는 의미로 default 메서드를 사용해야지 일반 클래스의 메서드처럼 상속해 줄 목적으로 default 메서드를 만들어서는 안된다.
  
default 메서드의 단점:
default 메서드는 이미 구현되어 있기 때문에 클래스에서 반드시 정의할 필요는 없다.
강제로 정의하게 만들 수 없는 것이 문제이다.

default 메서드의 용도:
기존에 정의된 인터페이스에 새 규칙을 추가할 때 기존 프로젝트에 영향을 끼치지 않기 위함이다.
따라서 새 인터페이스를 정의할 때는 default의 사용을 자제해야 한다.

 

인터페이스 caller / callee 

사용자가 <<caller>> Exam0110 작성 
<<interface>> Iterator 에 따라서 ... 
 => 인터페이스 호출 규칙에 따라 객체를 사용한다.
사용자가 <<callee> MyFilter 작성 .. 
File클래스의 list()메서드가 <<interface>> FileFitler -accept()
인터페이스 규칙에 따라 동작할 클래스를 만들것이냐 , , ,, , , , 
=> File의 list() 메서드가 사용할 필터 객체를 만드는 입장이다.
=> 개발자는 FilenameFilter 인터페이스 규칙 따라 클래스를 작성한다.
=> 이렇게 작성한 클래스는 list() 메서드에서 사용할 것이다.