2022-01-12(수) 9주차 3일

Jeongyun Heo·2022년 1월 11일
0

com.eomcs.oop.ex06.a.Car.java

다형성(polymorphism) : oop.ex06.a

여러 가지 형태

com.eomcs.oop.ex06.a.Exam0110.java

레퍼런스는 같은 타입의 객체를 가리킬 수 있을 뿐만 아니라
그 클래스의 서브클래스 객체까지 가리킬 수 있다.

파충류, 포유류 둘 다 동물의 특징을 갖고 있다.

Vehicle c1;
c1 = new Vehicle();
c1 = new Bike();

Vehicle에 있는 변수도 쓰겠다는 거고

Bike는 Vehicle보다 더 많이 갖고 있다.
그러니 아무런 문제가 없다.

상위 레퍼런스는 하위 레퍼런스를 가리킬 수 있다.
하위 클래스의 인스턴스 주소를 담을 수 있다.

✔ 상위 클래스의 레퍼런스는 하위 클래스의 인스턴스 주소를 담을 수 있다.
✔ 상위 클래스 레퍼런스는 하위 클래스의 인스턴스를 가리킬 수 있다.
✔ 상위 클래스 레퍼런스는 하위 클래스의 객체를 가리킬 수 있다.

하위 클래스 레퍼런스는 상위 클래스의 인스턴스 주소를 담을 수 없다.
아예 처음부터 컴파일 안 해줌

✔ 존재하지 않는 변수나 메서드를 사용할 수 있기 때문에 아예 원천적으로 컴파일을 막는 것이 프로그램 안정성에 도움이 된다.
그래서 하위 레퍼런스는 상위 클래스의 객체 주소를 담을 수 없다.
만약 컴파일을 허용한다면, Car의 인스턴스 멤버를 사용하려 할 것이다.

하위 클래스가 멤버가 더 많음
상위 클래스 멤버 + α

이게 다형적 변수의 특징

하위 클래스의 레퍼런스로 상위 클래스의 인스턴스를 가리킬 수 없다.
상위 클래스의 인스턴스에는 하위 클래스의 멤버가 없을 수 있기 때문이다.

com.eomcs.oop.ex06.a.Exam0210.java

Car c = new Sedan();

c.sunroof = true; // 컴파일 오류!
c.auto = true;    // 컴파일 오류!

c는 Car 클래스 레퍼런스이다.
따라서 Car 클래스 범위에 한해서 사용할 수 있다.
c가 비록 Sedan 객체를 가리키고 있더라도 문법적으로는 오류다.

컴파일러는 실행해보면서 문법적으로 옳다 그르다를 따지는 게 아니라
현재 명령문 자체가 오류인지 따진다.

자바 컴파일러는 레퍼런스가 실제 어떤 인스턴스를 가리키는지 따지지 않고
레퍼런스의 타입에 한정하여 인스턴스나 클래스의 멤버 사용을 허락한다.

c 라는 변수는 Car 클래스의 레퍼런스

레퍼런스 변수가 실제 가리키는 것이 무엇인지 알려줘야 한다.
((실제 레퍼런스가 가리키는 인스턴스의 타입) 레퍼런스).멤버

((Sedan)c).sunroof = true; // OK!
((Sedan)c).auto = true;    // OK!

또는 인스턴스의 원래 클래스 레퍼런스에 저장한 다음에 사용

Sedan s = (Sedan)c;
s.sunroof = true; // OK!
s.auto = true; // OK!

s에 들어 있는 주소나 c에 들어 있는 주소나 같은 주소
임시 변수 만들어서 담아서 쓴다

com.eomcs.oop.ex06.a.Exam0310.java

형변환(type casting)으로 컴파일러를 속일 수는 있지만,
실행할 때 오류가 발생할 것이다.

실제 메모리는 바뀌는 게 아님
그냥 던져주는 거

실행할 때 오류 발생! (runtime exception)
java.lang.ClassCastException

매번 명시하기 귀찮으니까 형변환 하는 거

실행 : runtime

com.eomcs.oop.ex06.a.Exam0410.java

그 타입의 객체 뿐만 아니라 하위 타입 객체도 담을 수 있다.

치와와 아리

PrintStream(OutputStream out)

public abstract class OutputStream ← 추상클래스

일반적인 클래스는 Concrete class (콘크리트 클래스) 라고 한다.

서브 클래스 중에서 인스턴스를 만들 수 있는 클래스로 인스턴스 만들어서 파라미터로 넘긴다.

두 개의 클래스가 같은 조상을 가질 때는 다형적 변수를 활용하라!

서브 타입 = 서브 클래스
타입 = 클래스

클래스, 인터페이스
두 개를 퉁쳐서 타입이라고 한다

파라미터 클래스라고 안 하고 파라미터 타입이라고 함

Car의 서브 타입이니까 올 수 있다

com.eomcs.oop.ex06.a.Exam0510.java

실제 어떤 객체가 있는지 궁금한 거

instanceof 연산자?
레퍼런스에 들어있는 주소가 특정 클래스의 인스턴스인지 검사한다.
또는 그 상위/하위 클래스의 인스턴스인지 검사한다.

모든 클래스 class 라는 스태틱 변수가 있다.
클래스 정보가 들어 있다.

정확하게 해당 클래스인지

getClass()
레퍼런스가 가리키는 인스턴스의 실제 클래스 정보를 리턴한다.
== 연산자를 사용하여 특정 클래스의 인스턴스인지 좁혀서 검사할 수 있다.

com.eomcs.oop.ex06.a.Exam0520.java
상위 타입으로 값을 받는 예

다형적 변수와 instanceof 연산자를 사용하면

com.eomcs.oop.ex06.b

다형성 - 오버로딩 (oop.ex06.b)

🔹 오버로딩
파라미터의 개수나 타입이 다르더라도 같은 일을 하는 메서드에 대해 같은 이름을 부여함으로써 프로그래밍의 일관성을 제공

호출할 때 일관성있게 호출하자

메서드를 파라미터로 구분한다.

argument : 파라미터에 전달하는 값

아규먼트의 타입과 순서, 개수에 맞는 메서드를 찾는다.

없으면 컴파일 오류

변수명만 다르게 하면 안 됨
변수명은 상관 없음

리턴 타입만 다른 메서드를 중복해서 만들 수 없다.

메서드가 리턴한 값을 받을지 안 받을지는

이미 수퍼 클래스에 같은 이름을 가진 메서드가 있는데 파라미터가 다름
이것도 마찬가지로 "오버로딩"이다.

파라미터 형식이 다르다면, 수퍼 클래스에 있는 메서드와 같은 이름을 가진 메서드를 추가해도 오버로딩이다.

수퍼 클래스에 있는 메서드와 이름이 같고 파라미터가 다르다.

파라미터가 같다면 재정의한다는 거

오버로딩 ← 과적재
오버라이딩 ← 재정의

파라미터 이름이 다른 것으로는 메서드를 구분할 수 없다.

접근 범위는 상관없다. private.. protected...

C에서는 불가능하다. 메서드 이름이 같으면 에러남
C++은 가능
C에는 패키지 라는 개념이 없음
조건을 붙여야 됨
eomplusf()
앞에 eom
붙여줌
C, C++에서는 네임스페이스
자바에서만 '패키지'라고 함
프로그래밍 언어에서는 '네임스페이스'라고 부른다.
메서드나 클래스를 그룹으로 묶을 때 네임스페이스라고 하지 패키지라고 안 함

이름 매번 새로 지어주는 것도 번거롭고 사용하기도 번거로움

아규먼트의 타입이나 개수에 따라 호출되는 메서드가 결정된다.

상수라고 하지 말고 리터럴이라고 해라

메서드를 찾을 때 아규먼트의 타입과 일치하는 메서드를 찾기 때문에
메서드의 파라미터 이름은 아무 상관이 없다.

서브 클래스에서 수퍼 클래스의 스태틱 메서드를 호출할 수 있다

다형성 - 오버라이딩(overriding)

com.eomcs.oop.ex06.c

🔹 오버라이딩(overriding)
상속 받은 메서드를 서브 클래스의 역할에 맞춰 재정의하는 것

인스턴스를 만든다는 것은 변수를 만드는 거

일관성 있게!

오버라이딩 메서드는 원래의 메서드보다 접근 범위가 같거나 커야 한다.

부모가 protected, 자식 public ← 가능

오버라이딩
super 클래스의 메서드를 서브 클래스의 역할에 맞게 재정의

프로그래밍의 일관성을 확보하는 문법이다.
동작이 일치
메서드가 동일하게 동작
음악이랑 미술이 추가되어도 동일하게 동작

method signature : 메서드명, 파라미터 타입/개수/순서
리턴타입은 따로

오버로딩과 오버라이딩 구분하기

com.eomcs.oop.ex06.c.Exam0130.java
필드 오버라이딩

필드 오버라이딩은 메서드와 달리 변수의 타입이 달라도 된다.
실무에서 안 씀

가능한 수퍼 클래스의 필드와 같은 이름을 가진 필드를 만들지 말라!

this.필드명
현재 클래스에서 해당 필드를 찾는다. 없으면 상위 클래스로 따라 올라가면서 찾는다.

super.필드명
상위 클래스에서부터 해당 필드를 찾는다. 없으면 계속 상위 클래스로 따라 올라간다.

수퍼 클래스에서 찾아(X) → 수퍼 클래스에서 찾아 올라가(O)
있을 때까지 찾아 올라가!

com.eomcs.oop.ex06.c.Exam0210.java

🔹 오버라이딩 조건
메서드명, 파라미터 형식, 리턴 타입이 같아야 한다.
파라미터의 이름은 달라도 된다. 상관없다.

안전장치 필요

오버라이딩 여부를 검사하는 특별한 문법을 추가하였다.

메서드 정의 앞에 @Override를 붙여라

@Override // <= 컴파일러야, 내가 상속받은 메서드를 재정의한다고 했는데, 혹시 실수는 없는지 검사해 줄래?

컴파일러나 JVM한테 얘기하는 특별한 주석

컴파일러한테 검사해달라고 요청

오버라이딩을 한다고 하면서 오버로딩이 되는 경우가 있기 때문에

save action
오버라이드 메서드인 경우에 자동으로 애너테이션 붙게 설정해놓음

변수 선언 앞이나 메서드 정의 앞에

멤버의 접근 범위
private : 같은 클래스
(default) : 같은 클래스 + 같은 패키지
protected : 같은 클래스 + 같은 패키지 + 서브 클래스
public : 모두

private은 오버라이딩 불가

접근 권한이 없는 메서드는 오버라이딩 불가!

protected 조금

컴파일 오류! 원래 접근 범위 보다 좁힐 수 없다.

접근 범위를 같게 하거나 확대하는 건 가능
원래 접근 범위 보다 좁힐 수 없다

오버라이딩 할 때 원본 보다 접근 범위를 좁힐 수는 없다.

com.eomcs.oop.ex06.c.Exam0410.java
오버라이딩과 super 키워드

411 예제 추가

오버라이딩 - super 키워드

com.eomcs.oop.ex06.c.Exam0410.java

this super 둘 다 obj 주소가 들어 있다.

this.m(); ← 현재 클래스(this의 실제 클래스)에서 메서드를 찾아 호출한다.
super.m(); ← 수퍼 클래스(메서드가 소속된 클래스의 수퍼 클래스)에서 메서드를 찾아 호출한다.

상속 받은 메서드를 호출할 경우

현재 클래스의 기준?
수퍼 클래스의 기준?

현재 클래스 ← this의 실제 타입 = test()를 호출할 때 넘겨준 객체의 타입 (A3)

A3부터 찾아 올라간다

수퍼 클래스 ← 메서드가 소속된 클래스(A2)의 수퍼 클래스 : A

this와 super 키워드

com.eomcs.oop.ex06.c.Exam0420.java

따라 올라간다!

this.m1();
this가 실제 가리키는 인스턴스 클래스를 기준으로 메서드를 찾아 올라 간다.

super.m1();
test()가 소속된 클래스를 기준으로 수퍼 클래스부터 메서드를 찾아 올라간다.

X4의 m1()
X2의 m1()
X3의 m2()
X3의 m2()

I18N : Internationalization
L10N : Localization

어휘력.. 소설, 책 읽기...

메서드가 소속된 클래스의 수퍼 클래스부터 찾아 올라간다

스트리밍
프로미스
fetch
컨베이어 벨트

super.super.m1(); // 컴파일 오류! 이런 문법은 없다!

호출하고 싶으면 다른 방식으로 호출하기

this와 super 키워드

com.eomcs.oop.ex06.c.Exam0421.java

X5의 m1()
X2의 m1()
X5의 m2()
X3의 m2()

com.eomcs.oop.ex06.c.Exam0430.java 혼자 보기

레퍼런스와 필드

03-OOP1 / 82 페이지

com.eomcs.oop.ex06.c.Exam0510.java
오버라이딩(overriding) - 레퍼런스가 가리키는 필드

형변환 클래스에 존재하지 않는 필드는 가리킬 수 없다.
System.out.println(((A)obj1).age); // 컴파일 오류!
A 클래스에 age 라는 필드 없음

레퍼런스와 메서드

03-OOP1 / 83 페이지

com.eomcs.oop.ex06.c.Exam0511.java
오버라이딩(overriding) - 레퍼런스가 가리키는 메서드

X4의 m1()
X4의 m1()
X4의 m1()
X4의 m1()
X4의 m1()
X4의 m1()
X4의 m1()

인스턴스 필드와 달리 메서드의 경우는
레퍼런스에 대한 형변환에 상관없이
실제 레퍼런스가 가리키는 클래스에서 메서드를 찾아 올라간다.

형변환 해도 실제 레퍼런스 변수가 가리키는 객체의 타입에서 찾아 올라간다.

오버라이딩과 리턴타입

03-OOP1 / 84 페이지

com.eomcs.oop.ex06.c.Exam0610.java
오버라이딩(overriding) - 리턴 타입

메서드 UML 표기 메서드명() : 리턴타입

create() : Car
↑ 메서드 오버라이딩
create() : Sedan
↑ 메서드 오버라이딩
create() : Tico

리턴 타입이 달라도 오버라이딩이 가능한가?
⇒ 서브 타입(클래스)인 경우만 가능

오버라이딩 메서드의 리턴 타입은 서브 타입(클래스)도 가능하다.

메서드명, 파라미터 타입, 순서, 개수가 같아야 한다.

메서드를 오버라이딩 할 때 리턴 타입으로 서브 클래스도 가능하다.

The return type is incompatible with Exam0610.CarFactory.create()

com.eomcs.oop.ex06.c.Exam0620.java 혼자 보기

oop.ex06.d

com.eomcs.oop.ex06.d.Exam0110.java
다형적 변수와 오버라이딩 - 레퍼런스와 메서드 호출

A2 클래스에서 먼저 m()을 찾는다

A obj = new A2();

레퍼런스가 하위 클래스의 인스턴스를 가리킬 때,
레퍼런스를 통해 호출하는 메서드는
레퍼런스가 실제 가리키는 하위 클래스에서 찾아 올라간다.

obj.x(); // 컴파일 오류!

그렇다고 해서 A2에서 추가한 메서드를 호출할 수는 없다.
레퍼런스의 클래스를 벗어나서 사용할 수는 없다.

컴파일러 입장에서는 obj는 A
레퍼런스 타입만 봄
실제로 어떤 걸 가리키고 있는지 관심 없음

((A2)obj).x(); // OK!

A2로 형변환을 수행한 후에는 A2의 멤버를 사용할 수 있다.

컴파일하고 실행하고 섞지 말기

컴파일 통과

컴파일러 입장에서 통과시키는 거랑 섞지 말기

com.eomcs.oop.ex06.d.Exam0210.java
다형적 변수와 오버라이딩 - 레퍼런스와 메서드 호출

오버라이딩 메서드 호출 규칙에 따라,
레퍼런스가 실제 가리키는 객체의 클래스부터 메서드를 찾아 올라간다.

추상클래스의 추상메서드 오버라이딩 그리고 레퍼런스

03-OOP1 / 85 페이지

<<abstract>> stereotype

<<concrete>>

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

Sedan 객체에 대해서 run()을 찾는다

실제 C가 가리키는 객체(Sedan)의 메서드 호출

Sedan에 있는 run 실행

오버라이딩 메서드 호출 규칙에 따라,
레퍼런스가 실제 가리키는 객체의 클래스부터 메서드를 찾아 올라간다.

oop.ex06.e

com.eomcs.oop.ex06.e.Exam0110.java
final 사용법: 상속 불가

클래스에 final을 붙이면 이 클래스의 서브 클래스를 만들 수 없다.
서브 클래스 생성을 방지하여 기존 클래스를 대체하지 못하도록 할 때 사용한다.

The type Exam0110 cannot subclass the final class A

com.eomcs.oop.ex06.e.Exam0210.java
final 사용법: 오버라이딩 불가

메서드에 final을 붙이면 서브 클래스에서 오버라이딩 할 수 없다.
서브 클래스에서 변경하면 안되는 메서드인 경우에 사용한다.
예) 보안에 관련된 일을 하는 메서드

final 메서드는 오버라이딩 불가!

일반 메서드는 오버라이딩 가능!

Cannot override the final method from B

지금 우리가 클래스나 메서드 앞에 final 붙일 일 없음..

다른 사람이 final 쓴 거 보고 어떤 의미인지 알 수는 있어야 함
상속받아서 기능을 확장하지 말라는 거네...

com.eomcs.oop.ex06.e.Exam0310.java
final 사용법: 상수 필드 만들기

필드에 final을 붙이면 상수 필드가 된다.

상수 필드는 값을 변경할 수 없다.

변수 초기화 문장
생성자 안으로 들어옴
변수 초기화 문장은 컴파일 될 때 생성자로 복사되기 때문이다.

변수 초기화 문장으로 값을 초기화시킬 수 있다.
복잡하지 않으면 생성자에서 하지 말고 그냥 한 줄로 쓰기

초기화 문장에서 값을 설정했으면, 생성자에서 다시 값을 설정할 수 없다.

초기화 문장은 결국 컴파일할 때 생성자의 첫 번째 문장으로 옮겨지기 때문이다.

com.eomcs.oop.ex06.e.Exam0330.java

인스턴스 초기화 블록에서 값을 초기화시켜도 된다. 초기화 블록의 코드 또한 생성자에 복사된다.

값을 한 번만 설정해야됨

상수 필드는 보통 클래스 필드(스태틱 필드)로 만든다.
클래스가 로딩될 때 딱 한 번만 만들어지게 한다.
보통 상수 필드는 public으로 공개한다.

로컬 변수에 final을 붙이면 값을 변경할 수 없는 상수로 사용된다.

com.eomcs.oop.ex06.e.Exam0510.java
final 사용법: 파라미터

파라미터 : 메서드가 일을 하는데 필요한 값을 받는 변수

보통 실무에서 파라미터를 final로 선언한다.

public void m1(final int a) {

데코레이터 패턴
주입

ex06 문법 숙지하기

0개의 댓글