JAN's History

자바의 정석 - CH7 객체지향 언어 2 - 3 본문

자바

자바의 정석 - CH7 객체지향 언어 2 - 3

JANNNNNN 2023. 4. 12. 17:04

1. 추상클래스

- 미완성 설계도. 미완성 메서드를 갖고 있는 클래스

추상 메서드를 포함하고 있으면 다 추상클래스!

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

=> 미완성 설계도는 제품 생산이 불가한 것처럼!

추상메서드 구현 = 추상메서드 몸통{}만들기.

더이상 추상메서드가 아니기 때문에 abstract을 붙이지 않음.

=> 인스턴스 생성 가능 

Ex) AudioPlayer(=>Player도 가능! 조상이니깐) ap = new AudioPlayer();

1-2 추상메서드 

- 미완성 메서드. 구현부(몸통, {})가 없는 메서드

When ? 

=> 꼭 필요하지만 자손마다 다르게 구현될 것으로 예상되는 경우

추상메서드는 나중에 상속을 통해 자손이 몸통을 만들어줘야한다.

2개 중 1개만 구현했을 때도 abstract을 붙여야한다. 

2개 다 구현해야 완전한 클래스!

 

- 추상 메서드 호출 가능(호출할 때는 선언부만 필요)

=> 나중에 상속을 통해 자손이 몸통을 완성한 후 즉, 객체가 생성된 후에야 호출이 가능함.

abstract을 붙이면 필수적으로 상속 받아 몸통을 생성해야하기 때문에 강제성 또한 코드에 내비출 수 있다.

1-3 추상클래스의 작성

- 여러 클래스에 공통적으로 사용될 수 있는 추상클래스를 바로 작성하거나

  기존 클래스의 공통 부분을 뽑아 추상클래스를 만든다. (더욱 간결해짐)

move()의 메소드 정의 방법은 해병, 비행기는 다 다를 수있으므로 추상클래스로 정의한 후 자손이 알아서

따로 메소드를 구현하도록 한다.

abstract을 안붙인 경우에는 에러는 발생하지 않지만 다른 사용자가 그 메서드를 사용하려 접근하는 과정에서 오류가 날 수 있고 (구현되지 않은 메서드라 사용이 불가하니까) 강제성이 없기 때문에 자손들이 메서드를 구현하지 않을 수 있다. 

abstract을 만들었다는 것은 리모콘에 버튼을 추가했다는 점과 같다.

abstract가 있기 때문에 group의 리모콘에 move가 생기는 것이고 그것이 가능해서 

for문에서 각 move에 100,200을 넣을 수 있게 되는 것이다.

for 문에서 move는 추상메서드가 아니라 각 구현된 메서드가 호출되는 것.

+ group에서 다형성의 2번째 장점 배열에 다양한 객체를 생성할 수 있다.

object (최고조상)으로 group가 생성될 경우에는 에러가 발생한다. object에는 move메서드가 없기 때문.

예제

맨 윗줄과 아래 4줄은 같은 코드!

한번에 배열의 생성과 초기화를 한 케이스. => 익숙해지자.

+ 추상클래스가 필요한 이유 정리

설계도가 3개가 있는데 안의 객체가 거의 비슷하다면? 코드의 중복!

그럴 경우엔 중복된 코드를 하나의 객체로 빼고, 미완성 설계도로 분류한다.

이후 자손을 생성해 부모로 미완성 설계도를 받아 완성된 설계도가 되게 하면 끝!

만약 새로운설계도를 만들고 싶다면 하나의 완성된 설계도를 또 생성하면 된다.

코드의 관리가 용이해진다.

=> 자손이 생성될 수록 단계적으로 완성이 되어간다.

1-4 구체화(명확, 구체적) Vs 추상화(불명확)  

 

첫 번째 줄 코드는 참조변수가 일치하고 어떤 클래스를 받을 것인지 명확하게 선언되어있다.

두 번째 줄 코드는 Calendar객체를 반환하여 참조변수를 cal로 받는데 무엇을 반환할지 나와있지 않다. (불분명) => 추상

GregorianCalendar cal = new GregorianCalendar (); 은 구체적 객체를 생성해서 나라마다 코드를 변경해야한다.

그러나 Calendar cal = Calendar.getInstance(); 처럼 애매하게 코드를 작성하면 더 유연하고 변경에 유리하다.

=> 만약 수정이 생겼을 시 첫 번째는 캘린더가 사용된 곳 마다 다 수정을 해야하지만 두 번째는 이 메서드 하나만 바꾸면 됨

2. 인터페이스

- 추상 메서드의 집합

- 구현된 것이 전혀 없는 설계도. 껍데기 (모든 멤버가 public)

추상클래스는 일반 클래스이며 추상 메서드를 가지고 있는 것이고, iv도 있고 생성자도 있음.

인터페이스는 추상 메서드만 가지고 있는 것

.저 캡슐화에서 메서드만(즉, 껍데) 가지고 있는 것!

iv, cv 변수는 가질 수 없고 상수 (=final)값만 가질 수 있다.

interface에는 public이 생략되어있다. 

avstract도 생략 가능하다.

2-2 인터페이스의 상속

- 인터페이스의 조상은 인터페이스만 가능(object가 최고 조상이 아님)

- 다중 상속이 가능(조상이여러개)

fightable은 몸통에 멤버가 정의되어있지 않지만 상속받았기 때문에 사실은 멤버가 2개인 것

2-3 인터페이스의 구현

- 인터페이스에 정의된 추상 메서드를 완성하는 것

추상클래스는 extends로 상속을 받아 완성하지만 interface는 implements를 통해서 구현한다.

+ 인터페이스의 구현 = 몸통 만들기 {} (미완성 설계도 완성하기)

인터페이스 구현 과정
일부만 구현하는 경우

2개 중 하나만 구현하는 경우 (=즉 일부만 구현하는 경우엔) 앞에 abstract를 붙여야한다.

안써있지만 사실은 아래에 public avstract void attack(Unit u);가 생략되어있는 것

Q. 추상 클래스와 인터페이스의 공통점

A. 추상메서드를 가지고 있다 (미완성 설계도)

Q.추상 클래스와 인터페이스의 차이점

A. 인터페이스는 iv를 가질 수 없다.

2-4 인터페이스를 이용한 다형성

- 인터페이스도 구현 클래스의 부모? Yes

인터페이스 참조변수로 그 인터페이스를 가르키는 다형성이 가능하다.

But 인터페이스의 리모콘 버튼 갯수가 자손보다 적어야함 (이거는 그냥 다형성의 원칙과 동일)

void attack(Fightable f) => 매개변수의 타입이 인터페이스인 경우!

입이 Fightable이므로,  Fightable인터페이스를 구현한 클래스의 인스턴스만 가능하다는 뜻

 

1. 인터페이스 타입의 변수로 인터페이스를 구현한 클래스의 인스턴스를 참조할 수 있다.

2. 인터페이스를 메서드의 매개변수 타입으로 지정할 수 있다.

 

3. 인터페이스를 메서드의 리턴타입으로 지정할 수 있다.

인터페이스의 참조변수로 자손객체를 가리키는 것! (다형성)

메소드의 반환타입이 인터페이스(=Fightable)면 인터페이스를 구현한 객체를 return(=반환)해야한다.

=> Fighter(자손)는 Fighterable(조상)으로 형변환이 가능하기 때문

메서드를 호출한 쪽에서는 반환타입과 일치하는 혹은 자동형변환이 가능한 타입의 변수의 결과를 저장해야한다.

예제

그리고 이 3개의 값의 타입은 일치해야한다.

2-5 인터페이스의 장점

  • . 두 대상(객체)간의 '연결, 대화, 소통'을 돕는 '중간 역할'을 한다.

예시

껍데기 = 인터페이스

  •  선언(설계, 껍데기)와 구현(알맹이)을 분리시킬 수 있게 한다.

=> 알맹이만 바꿀 수 있기 때문에 변경에 유리하다.

껍데기는 동일하고, 자판기 안에 기계가 바뀌었을 때 우리는 기계가 바뀐지 모르는 것처럼

인터페이스 덕분에 B가 변경되어도 A는 안바꿀 수 있게 된다.

=> A 변경 불필요! (느슨한 결합) , 왼쪽은 강한 결합.

 

코드로 설명

직접적인 관계의 두 클래스 A-B에서는 B를 직접적으로 사용하기 때문에 B를 C로 수정할 때 A도 같이 수정해야한다.

그러나 간접적인 관계에서는 A가 I와만 연관이 있기 때문에 B가 C로 변해도 A는 변경이 없다.

예제

[장점]

1. 개발 시간을 단축할 수 있다. 

A가 B가 완성되어있지 않고도 인터페이스를 이용해 코드를 작성할 수 있다.

=> 추상메서드니까 완성되어있다고 생각하고 가정하고 호출할 수 있기 때문.

2. 변경에 유리한 유연한 설계가 가능하다.

B대신 C로 바뀌어도 유연하게 대처 가능

3. 표준화가 가능하다.(JDBC)

=> JAVA가 JDBC라는 인터페이스를 지키도록 오라클이던 MySQL이던 DB프로그램에 공지하면 

자판기처럼 우리가 사용한 코드들이 JDBC를 지키는 한, 오라클에서 MySQL로 바뀌어도 프로그램을 거의 변경 X

4. 서로 관계없는 클래스들을 관계 맺어줄 수 있다.

여기서 SCV, Tank, Dropship을 고치고 싶다.

그러나 따로 객체를 오버로딩하여 생성하면 너무 비효율적이니 Repairable이라는 인터페이스를 

갖게해 공통점을 부여하고 구현하게 만드는 것.

이렇게 되면 Repairable 인터페이스를 구현한 구현체로 넘이 생성되고, 따로따로 repair 메서드를 적을 필요 X

인터페이스를 이용한 코드

Repairable r = > 인터페이스 Repairable 를 구현한 객체만 들어올 수 있음. 즉, SCV, Tank, Dropship

2-6 디폴트메서드

- 인터페이스에 디폴트 메서드, staitc 메서드 추가 가능.

- 인터페이스에 새로운 메서드(추상 메서드)를 추가하기 어려움.

- 디폴트 메서드는 인스턴스 메서드(인터페이스 원칙 위반)

=> 기존의 인터페이스에 추상 메서드를 추가하기 위해선, 구현체에 또 구현을 해야하는데 

구현체가 많을 수록 그 파장은 더욱 심해지기 때문에 인터페이스 내에서 몸통을 만들 수 있게 한 것이 default.

=> 그냥 오버라이딩 하나면 해결.

2-7 내부 클래스

- 클래스 안에 선언된 클래스

=> 내부 클래스의 장점 : 내부 클래스에서 외부 클래스의 멤버들을 쉽게 접근할 수 있다. =>원래는 A객체를 생성해야함.

   : 코드의 복잡성을 줄일 수 있다 (캡슐화) => B클래스를 사용할 수 없기 때문.

내부클래스 생성 전과 후

BBB가 AAA의 내부 클래스로 들어감으로써 원래는 a.i로 접근해야하는 것이

객체 생성 없이 외부 클래스의 멤버가 i로 바로 접근이 가능해졌다.

또한 클래스 BBB가 내부로 들어감으로써 보기에 더 깔끔한 코드가 된다.

- 내부클래스의 종류와 유효범위는 변수와 동일하다.

익명클래스 : 클래스의 선언과 객체 생성을 동시에 하는 이름없는 클래스(일회용)

static은 객체 생성 없이 바로 사용 가능해야하기 때문에 static 멤버를 정의하고 싶다면 클래스도 static이어야한다.

=>static 내부 클래스에서는 외부 클래스의 인스턴스 멤버에 접근할 수 없다. 

인스턴스 멤버는 같은 클래스 내에서만 사용 가능하기 때문 !  

그래서 iv - > cv를 사용 가능하지만, cv는 iv를 사용할 수 없다.

예제 1

상수는 static 사용 가능.

final static 을 사용하는 경우 => ex) final static CARD_NUM. 카드마다 다른 값이 아니라 객체와 상관 없이 고정값일때

//이런 케이스가 가장 많

final만 사용하는 경우 => ex)final int kind, num처럼 CARD에서 무늬, 숫자와 같은 고정이지만 카드마다 다를 때

static을 사용하는 경우 => ex)final height = 200; width = 100l처럼 공통된 변수 값을 가질 때

실행할 때

println(LocalInner.CONST)는 에러 발생 

=> 지역 내부 클래스의 static 상수는 메서드 내에서만 사용할 수 있기 때문에 메서드 밖에서는 사용 불가.

예제 2

static 멤버는 공유변수이기 때문에 인스턴스 멤버가 접근 가능. 

But static 멤버는 인스턴스 멤버에 접근 불가. => 인스턴스 멤버는 클래스 내에서만 사용 가능하기 때문.

그래서 static StaticInner cv = new InstanceInner();은 에러 발생.

예제 3

외부 클래스의 private도 내부 클래스에서 접근 가능.

But static 내부에서는 private이 인스턴스 변수라서 접근 불가.

지역 내부 클래스에서는 final 이 붙은 상수만 접근 가능하다.

=> lv는 메서드가 끝남과 동시에 사라지는 변수이기 때문에 접근 불가.

예제 4

1. 내부 클래스를 사용하고 싶다면 외부 클래스의 인스턴스를 먼저 생성하고. oc.new 를 통해 내부 클래스의 객체를 생성!

2. static 클래스는 외부 클래스의 객체 생성 없이 바로 사용 가능 (공유 변수이기 때문)

3. static 클래스의 인스턴스는 외부 클래스를 먼저 생성하지 않아도 된다.

=> 즉, Outer2 oc = new Outer2(); 처럼 안만들고 바로 Outer.StaticInner();을 사용해도 괜찮다는 뜻.

예제 5

외부 클래스의 iv와 내부클래스의 iv, 메소드의 lv의 이름이 같을 경우.

this.은 내부클래스와 지역변수의 값을 구분하기 위한 참조변수이므로 this.value은 20을 가리킨다.

Outer.this.value는 10.

2-8 익명 클래스

- 이름이 없는 일회용 클래스. 정의와 생성을 동시에

문법이니까 외우자 .

=> 이름이 없기 때문에 조상 클래스의 이름을 쓰거나 구현인터페이스의 이름을 쓴다

예제

원래는 클래스를 정의하고 객체를 생성하여 EventHandler()를 사용했지만 익명 클래스를 사용하면

객체를 생성할 필요 없이 조상 또는 인터페이스의 이름을 사용하고, 그 안에 내용을 적으면 된다.

=> 클래스의 정의와 객체 생성을 동시