어제 한 거
공통 분모 뽑아서 추상클래스 만듦
save() 메서드 추상 메서드
추상클래스와 추상메서드 활용 해봄

com.eomcs.oop.ex07

com.eomcs.oop.ex07.a.Exam01.java

추상클래스 목적
서브클래스에게 공통 필드나 메서드를 상속해주는 것이 목적이다.
추상메서드가 있든 없든 추상클래스로 만들 수 있다.
직접 사용하지 않는다.
상속에서 generalization을 통해 수퍼클래스를 정의하는 경우
그 수퍼클래스를 주로 추상 클래스로 만든다.
추상클래스는 인스턴스를 생성할 수 없다.

클래스는 타입이다.

추상클래스 레퍼런스
추상클래스를 상속 받아서 만든 서브클래스의 인스턴스를 받겠다는 거

com.eomcs.oop.ex07.a.Exam02.java

추상메서드
서브클래스마다 구현 방법은 다르다
반드시 정의해야 됨
구현하지 않으면 그 서브클래스도 추상클래스가 되어야 됨

서브클래스가 반드시 구현해야 하는 메서드가 있다면 추상메서드로 선언하라!
강제하는 효과가 있다.

com.eomcs.oop.ex07.a.Exam03.java

추상클래스는 인스턴스 생성 불가

컴파일러는 알고 있다
obj에 들어 있는 것은 A3 객체가 아니라 A3의 서브클래스의 객체인 걸 알고 있다.
A3 객체가 절대 들어갈 수 없는 걸 아니까.

A3Sub부터 찾아 올라간다.
레퍼런스가 실제 가리키는 객체의 클래스에서부터 메서드를 찾는다.

JVM이 그 인스턴스에 실제로 들어 있는 클래스부터 찾아간다.

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

static void test(A3 obj) {
static void test(빵 obj) {

빵은 실체가 아니지만 종류를 통칭해서 가리키는 공통 분모

레퍼런스 타입이 추상 클래스라는 건
추상클래스의 인스턴스를 받겠다는 게 아니라
추상클래스를 상속받은 서브클래스의 인스턴스를 받겠다는 거다.

com.eomcs.oop.ex07.a.Exam04.java

추상클래스의 용도는
서브클래스들이 가져야할 공통 변수나 메서드를 제공하는 것이다.
일반 변수나 메서드를 정의할 수 있다.

추상메서드
서브클래스마다 구현이 다를 경우
서브클래스마다 동작 방식이 다르기 때문에
수퍼클래스에서 구현하지 말고 미구현 상태로 둔다.

com.eomcs.oop.ex07.a.Exam05.java

추상클래스

03-OOP2 / 18 페이지

수퍼클래스에서 기능이 어떻게 동작하는지 정의한다.

✓ 수퍼 클래스에서 기능의 흐름을 정의한다.
기능의 흐름 : 틀(템플릿)을 구성
the skeleton of an algorithm

구체적은 구현은 서브 클래스에 맡긴다.

✓ 템플릿 안에서 실행하는 세부 동작은 서브 클래스에게 구현을 맡긴다.

수퍼 클래스는 실행 흐름을 정의하고 서브 클래스로 구체적인 동작을 정의
이런 설계 기법을 템플릿 메서드 패턴(template method pattern)이라고 한다.

템플릿 메서드 : 흐름, 틀만 잡아주는 메서드

concrete behavior

https://en.wikipedia.org/wiki/Template_method_pattern

🔹 Concrete Class
‐ 인터페이스를 구현해서 인스턴스를 만들 수 있는 클래스
‐ 추상 클래스를 상속받아서 추상 메서드를 구현했기 때문에 인스턴스를 만들 수 있는 클래스

알고리즘 : 동작 방식, 동작의 흐름

흐름은 수퍼 클래스
세부적인 동작은 서브 클래스

실제 this에 저장된 주소로 가서 그 클래스의 printHeader()를 호출한다.

this가 어느 인스턴스를 가리키느냐에 따라서

this 생략 가능
자동으로 붙음

컴파일하고 실행을 나눠서 생각하기

JVM이 메서드 호출해서 실행
실제 그 주소로 찾아서 어떤 클래스인지 인스턴지 확인해서 그 클래스에서 찾는다.

항상 메서드를 호출할 때는 반드시 둘 중에 하나가 있어야 됨
클래스.메서드(); ← 스태틱 메서드
인스턴스주소.메서드(); ← 인스턴스 메서드

로컬 변수가 아니라 클래스 변수나 인스턴스 변수면

this가 생략됐거나 클래스명이 생략된 거
스태틱 메서드면 클래스명이 생략된 거고
인스턴스 메서드면 this가 생략된 거

컴파일러는 찾아 올라가는 게 아님

컴파일러의 동작과 JVM의 동작은 다름

컴파일 할 때는 반드시 그 타입에 존재하는 메서드여야 한다.
A3 obj;
obj.m2(); // 컴파일 오류! A3에 m2() 메서드 없음!

컴파일러는 메서드가 추상메서드냐 아니냐는 안 따짐
메서드가 있냐 없냐를 따짐

추상클래스는 완성된 메서드를 물려줄 수 있음

com.eomcs.oop.ex07.b.Exam01.java

Arrays.copyOf(배열, 사이즈)
같은 값을 갖는 배열

https://visualgo.net/en/sorting

버블 정렬 : 순서대로 두개씩 비교
0번 - 1번 비교, 1번 - 2번 비교, 2번 - 3번 비교, ...
큰 숫자가 뒤로 간다
실행횟수가 너무 많음

퀵정렬
자기보다 작은 걸 만나면 교체

추상 클래스가 필요한 이유에 집중하기

실행속도가 완전 다름

정렬 알고리즘을 공부하는 이유
훈련하고 싶은 거
배열을 다루는 방법, 조건문, 반복문
알고리즘으로 공부
문법을 다루는 것도 공부하고
어떤 방법을 쓰느냐에 따라서 효율이 극도로 달라지는구나
겸사겸사 두 가지 공부하려고 알고리즘 공부함
프로그래밍 언어 문법을 다루는 방법을 공부

com.eomcs.oop.ex07.c.Exam01.java

추상클래스와 추상메서드의 활용

① 다른 타입의 클래스 사용

BubbleSort
QuickSort

두 개의 정렬 클래스는 같은 부류가 아니기 때문에
한 개의 display 메서드를 사용할 수 없다.
또한 사용법도 다르다.

개선

② 같은 타입의 클래스로 묶기

Sorter
sort()

QuickSort
start() ← sort()를 오버라이딩 하지 않았다.

display(Sorter sorter, int[] values)
같은 타입이라서 display()를 두 개 만들 필요가 없다.
클래스에 상관없이 같은 이름의 메서드 호출 ⟹ 일관된 사용 가능

추상메서드가 아니라 강제 사항이 아님
수퍼클래스 상속받은 거 오버라이딩 안 함
문법적으로는 문제가 없음

정렬이 안 됨

반드시 오버라이딩 해야 되는 메서드가 있다면 문법적으로 하라고 했어야 함
그래서 추상메서드
강제하게 한다

com.eomcs.oop.ex07.c.Exam02.java

Sorter는 두 개를 같은 타입으로 묶기 위한 용도
못 쓰게 했어야 함
그래서 추상클래스 등장
추상클래스로 선언했다면 이런 코드 못 짰을 거
추상클래스 존재 이유임

이렇게 문법을 느슨하게 하면 개발자의 실수를 막을 수 없음

추상클래스와 추상메서드의 활용

com.eomcs.oop.ex07.d

③ 수퍼클래스를 추상클래스로 만들기

<<abstract>> Sorter

com.eomcs.oop.ex07.d.Exam01.java

Sorter 클래스를 추상 클래스로 선언했기 때문에
이제 Sorter의 인스턴스를 생성을 막을 수 있다.

MergeSort ← 이 클래스의 sort를 오버라이딩 하지 않았다.
수퍼클래스의 sort() 메서드는 일반 메서드이기 때문에 오버라이딩이 필수가 아니다!

이런 실수나 의도를 방지하고자 등장한 문법이 "추상 메서드"이다.

서브 클래스에게 구현을 강제할 필요가 있을 때 추상 메서드로 선언하라!

④ 추상메서드로 오버라이딩을 강제하기

<<abstract>> Sorter
abstract sort()

반드시 추상메서드를 오버라이딩 해야 한다!

실제 레퍼런스가 가리키는 인스턴스의 클래스에서 메서드를 찾아 올라간다.

com.eomcs.oop.ex07.f

추상 클래스 대신 인터페이스를 쓸 때

03-OOP2 / 21 페이지

인터페이스로 클래스 묶기

물려줄 게 없음
추상클래스가 할 일이 없음
메서드 구현 강제하는 거 말고는 없음

추상 메서드만 있을 경우,
객체 사용 규칙을 정의하는 "인터페이스" 문법으로 바꿔도 좋다.

모든 메서드는 기본이 public 이고, abstract 이다.

다음과 같이 메서드 선언에서 public과 abstract를 생략해도 된다.

<<interface>> Sorter
abstract sort();
점선으로 바뀜
추상메서드는 반드시 구현해야 한다.

인터페이스로 바꾸는 게 훨씬 유연해진다.

오버라이딩 메서드는 접근 범위를 같게 하거나 확장하는 건 가능
좁게 하는 건 불가능

인터페이스 사용 전 : com.eomcs.oop.ex09.a1.before

03-OOP2.pdf / 22 페이지

객체(클래스)마다 메서드 시그너처(signature)가 다르다.
여기서 객체는 클래스. 문맥에 따라 해석해야 한다.
→ 객체 사용법이 다르다
→ 프로그래밍에 일관성이 없다
→ 유지보수가 어렵다

유사한 일을 하는데 객체 사용법이 다르다면 쓰기가 매우 불편하다.

유사한 일을 하는 객체에 대해 사용법을 통일하자.

이렇게 객체의 사용 규칙(호출 규칙)을 정의하는 문법이 "인터페이스(interface)"이다.

com.eomcs.oop.ex09.a1.after

03-OOP2.pdf / 23 페이지

<<interface>> Worker
execute()

메서드가 같기 때문에 프로그래밍 하기 편하다.

인터페이스 입장에서
규칙에 따라서 호출하는 자 caller Exam01
규칙에 따라서 호출되는 자 callee BlueWorker, WhiteWorker, JubuWorker
누가 호출자이고 누가 피호출자인지 확인하라.
본인이 맡은 개발 일이 호출자를 만드는 것인지 아니면 피호출자를 만드는 것인지 확인하라.
다 만드는 건 거의 드물다.
이 규칙에 따라서 만들거나 사용하거나 둘 중 하나

public 외에 다른 접근 범위는 사용할 수 없다.
(규칙은 공개되어야 하니까!)

클래스를 상속받은 서브클래스
인터페이스를 구현한 클래스가 됐든
상관없이 타입이라고 퉁친다

인터페이스 사용 전/후

com.eomcs.oop.ex09.a2.before

ToolA
m1()

ToolB
m2()

도구마다 사용법이 다르다
사용법: m1(), m2() 메서드 호출

프로그래밍에 일관성이 없다
유지보수가 힘들다

규칙은 공개되어야 하기 때문에 무조건 public 이다.
규칙이기 때문에 당장 구현할 필요는 없다. 그래서 추상 메서드이다.

// 물론 다음과 같이 public과 abstract를 모두 생략할 수 있다.

public 보다 낮은 접근을 지정할 수 없다.
즉 무조건 public 메서드이다.

인터페이스에도 상속이라는 단어 나와도 당황하지 말기

② 사용 후

<<interface>> Spec

인터페이스 레퍼런스
Spec 인터페이스를 구현한 클래스의 인스턴스 주소를 저장하겠다는 의미다.

미구현

com.eomcs.oop.ex09.b.Exam01.java

public, abstract 모두 생략할 수 있다.

인터페이스의 메서드를 재정의하는 것도 @Override

public 보다 접근 범위를 좁힐 수는 없다.

한 개라도 빠뜨린다면 concrete 클래스가 될 수 없다.

인터페이스 필드는 public static final 이다.
값을 한 번 정하면 못 바꿈

public, static, final 을 생략할 수 있다.
중요한 건 생략되었다는 뜻

상수이기 때문에 초기화 문장으로 적어야 한다.
인터페이스의 변수는 무조건 상수

public이면서 static이면서 final이다.

com.eomcs.oop.ex09.b.Exam03.java - java-lang/src/main/java

클래스 변수도 레퍼런스로 호출할 수는 있음. 하지만 지양해야 됨.

default method

새 규칙이 추가되면서 기존에 구현했던 클래스에서 컴파일 오류 발생!

🔹 default method
기존의 구현 클래스에 영향을 주지 않으면서 새 규칙을 추가하고 싶을 때

기존 클래스에 영향이 없도록 새 규칙을 정의할 때 메서드를 구현 상태로 만든다.

default 표시를 붙인다.

기존의 클래스는 새로 추가한 메서드를 그대로 상속받는다.
구현된 메서드이기 때문에 기존 클래스의 코드를 변경할 필요가 없다.

default 메서드는 강요를 안 함

default method의 문제점

새 구현체(implementor) : 인터페이스를 구현한 클래스의 줄임말

<<concrete>> Morning
start()
run()
stop()
추상메서드이기 때문에 반드시 구현해야 한다.
dump()는 이미 구현된 메서드이기 때문에 오버라이딩 하지 않아도 된다.
메서드 구현을 강제하지 못한다.
dafault 메서드 문법의 단점

인터페이스의 강제성을 약간 느슨하게 만들어버린다.
희석시키는 문제
함부로 아무때나 쓰지 말기

추상 메서드는 반드시 구현해야 한다.

default 메서드는 오버라이딩 해도 되고 안해도 된다.

m3는 구현하지 않아서 인터페이스에 정의한 거 호출

default method가 나온 이유 기억하기

com.eomcs.oop.ex09.b.Exam04.java

인터페이스 내부에서 사용할 메서드라면
private 접근 범위를 갖는
구현 메서드를 정의할 수 있다.

우리는 이거 쓸 일 없음

    // 인터페이스에 선언된 다른 default 메서드를 호출하고 싶다면,
    MyInterface4.super.m2();

인터페이스에 원래 있던 default 메서드를 호출하고 싶음

MyInterface4.super.m2();
MyInterface4 이거 안 붙이면 부모클래스 m2()를 호출한다

super와 인터페이스.super

인터페이스에 있는 메서드를 가리키려면

this가 생략됨
super.m1();
MyInterface.super.m1();

인터페이스에 정의된 private 메서드는 호출할 수 없다.

com.eomcs.oop.ex09.b.Exam05.java

인터페이스도 클래스처럼 static 메서드를 정의할 수 있다.

public이 생략된 거

확실한 목적이 없으면
도저히 해결 안 되는 상황이 아니면
스태틱 변수 쓰지 말기..

com.eomcs.oop.ex09.c.Exam0110.java

인터페이스도 다른 인터페이스를 상속 받을 수 있다.

ProtocolA에 있는 것도 구현해야 되고
ProtocolB에 있는 것도 구현해야 되고
ProtocolImpl

ProtocolImpl 클래스가 ProtocolB 의 규칙에 따라 제작되었다면
결국 그 수퍼 인터페이스 ProtocolA의 규칙도 준수하는 것이 된다.

com.eomcs.oop.ex09.c.Exam0120.java

자바는 클래스를 상속받을 때는 같은 시그너처를 가진 메서드가 있을 수 있기 때문에 다중 상속을 허락하지 않는다.

인터페이스는 다중 상속을 허락한다
왜?

클래스 다중 상속과 인터페이스 다중 상속

03-OOP2 / 30 페이지

어느 클래스로 가느냐에 따라 동작이 달라짐
C1의 m1()을 호출하느냐, C2의 m2()를 호출하느냐에 따라 동작이 달라진다
왜?
m1()은 구현된 메서드이기 때문이다.
⟹ 그래서 자바는 다중 상속을 허락하지 않는다

인터페이스 다중 상속

03-OOP2 / 31 페이지

I1 - m1(), m2()
I2 - m1(), m3()
I3 - m4()

m1() 메서드처럼 여러 수퍼클래스에 존재하더라도
어차피 구현하지 않은 메서드이기 때문에
둘 중 어떤 것을 상속받더라도 문제될 게 없다.
⟹ 그래서 인터페이스는 다중 상속을 허락한다.

MYClass
m4()
m1()
m2()

interface ProtocolC extends ProtocolA, ProtocolB {

com.eomcs.oop.ex09.c.Exam0120.java

인터페이스 다중 상속 불가

03-OOP2 / 32 페이지

com.eomcs.oop.ex09.c.Exam0140.java
인터페이스 다중 상속 불가

I1m1()I2m1()의 메서드 시그너처가 다르다.
특히 리턴 타입이 다르다.
이런 경우는 어떤 인터페이스를 상속받느냐에 따라 구현코드가 달라지기 때문에
혼동을 피하기 위해서 다중 상속을 허락하지 않는다.

메서드 시그너처(이름, 파라미터)는 같지만
리턴 타입이 다르다면 다중 상속이 불가능하다.

The return types are incompatible for the inherited methods Exam0140.ProtocolA.rule0(), Exam0140.ProtocolB.rule0()

인터페이스 다중 구현

03-OOP2 / 33 페이지

com.eomcs.oop.ex09.c.Exam0210.java

클래스는 다중 상속이 안 되지만 인터페이스는 다중 상속이 됨

I1, I2 모두 m3()의 시그너처가 같다면 한 번만 구현하면 된다.

메서드 시그너처(이름, 파라미터, 리턴타입)만 같으면 다중 구현에 문제가 없다.

인터페이스 다중 구현 불가

03-OOP2 / 34 페이지

com.eomcs.oop.ex09.c.Exam0230.java

메서드 시그너처에서 리턴 타입이 다른 경우
두 개의 인터페이스를 모두 만족시킬 수 없다.
그래서 다중 구현 불가!

메서드 오버로딩 문법상 리턴 타입만 다른 메서드를 동시에 정의할 수 없다.

com.eomcs.oop.ex09.c.Exam0231.java

메서드의 시그너처가 다르다면 오버로딩 가능하므로 다중 구현 가능
파라미터의 개수, 타입, 순서가 다르면 메서드 오버로딩 가능

com.eomcs.oop.ex09.c.Exam0310.java
디폴트 메서드의 다중 구현

인터페이스들에 같은 시그너처를 갖는 default 메서드가 여러 개 있을 경우,
어떤 메서드를 상속받느냐에 따라 실행 결과가 달라지기 때문에
다중 클래스 상속이 불가능한 것처럼
이 경우에도 다중 인터페이스 구현이 불가능하다.

그러나, 다음과 같이 클래스에서 default 메서드를 오버라이딩 한다면,
어차피 인터페이스에서 구현한 default 메서드를 사용하지 않기 때문에
이 경우에는 다중 구현이 가능하다.

인터페이스와 추상 클래스의 콜라보

03-OOP2 / 35 페이지

com.eomcs.oop.ex09.d

실무에서 가장 많이 쓰는 기법

<<interface>> ProtocolA
rule1()
rule2()
rule3()
rule4()

<<concrete>> MyClass
rule1() {...}
rule2() {...}
rule3() {...}
rule4() {...}
인터페이스의 메서드를 모두 구현

<<abstract>> AbstractProtocolA
rule1() {}
rule2() {}
rule3() {}
rule4() {}
추상클래스 이름은 보통 Abstract로 한다
그냥 구현한다는 것에 초점을 맞춰서 빈 채로 구현한다.

<<concrete>> MyClass
rule3() {...}
오버라이딩이 필요한 메서드만 정의할 수 있다.
인터페이스를 간접적으로 구현한다.

인터페이스 규칙 중 필요한 규칙만 구현할 수 있다.

수퍼 클래스가 인터페이스를 구현했다면,
그 서브 클래스는 자동으로 인터페이스를 구현한 것이 된다.

인터페이스와 추상 클래스의 콜라보 예

03-OOP2 / 36 페이지

com.eomcs.oop.ex09.d.Exam0130.java

인터페이스를 구현한 클래스를 상속받는다면 결국 인터페이스를 구현한 것이 된다.
CarSpec을 미리 구현한 AbstractCar를 상속받는 것이 클래스를 만들기가 편하다.

수퍼 클래스에서 미리 구현했기 때문에 서브 클래스에서 다시 구현할 필요가 없어 편하다!

서브 클래스는 수퍼 클래스가 구현하지 않은 나머지 메서드만 구현하면 된다.

인터페이스를 직접 구현할 경우 모든 메서드를 다 구현해야 한다.

남아 있는 추상메서드만 구현 → 프로그래밍이 편하다!

인터페이스를 추상클래스로 구현해놓고 일부만 서브클래스에서 구현

서로 다르게 구현해야 되는 메서드는 남겨 둔다

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/AbstractList.html

AbstractList ← 인터페이스 List 구현

Vector도 AbstractList를 상속받음

ArrayList, LinkedList, Vector 3개 모두 List 인터페이스를 구현한 거

List a = new ArrayList();
List b = new LinkedList<>();
List c = new Vector<>();

com.eomcs.oop.ex09.j.Exam0110.java
com.eomcs.oop.ex09.j.Exam0120.java
내일 한 번 더 하겠음

com.eomcs.oop.ex09.e ~ i 혼자 공부하기

0개의 댓글