oop ex07
추상클래스
클래스 앞에 abstract 붙이기.
목적: 서브 클래스에게 공통 필드나 메서드 등을 상속해주는 역할, 여러 클래스를 같은 타입으로 묶을 때.
추상클래스는 인스턴스를 생성할 수 없다. 레퍼런스는 생성할 수 있다.
A를 상속받아 레퍼런스 생성가능.
추상클래스의 추상메서드
메서드 선언부에 abstract 붙이기.
메서드 바디가 없다 { }
추상클래스나 인터페이스에서만 선언 가능.
서브 클래스마다 구현이 다를 수 있는 경우에 사용.
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() 메서드에서 사용할 것이다. |
'[네이버클라우드] 클라우드 기반의 개발자 과정 7기 > 웹프로그래밍' 카테고리의 다른 글
[NC7기-51일차(7월6일)] - 웹프로그래밍 32일차 (0) | 2023.07.06 |
---|---|
[NC7기-50일차(7월5일)] - 웹프로그래밍 31일차 (0) | 2023.07.05 |
[NC7기-48일차(7월3일)] - 웹프로그래밍 29일차 (0) | 2023.07.03 |
[NC7기-47일차(6월30일)] - 웹프로그래밍 28일차 (0) | 2023.06.30 |
[NC7기-46일차(6월29일)] - 웹프로그래밍 27일차 (1) | 2023.06.29 |