2022-02-11(금) 13주차 5일

Jeongyun Heo·2022년 2월 11일
0

com.eomcs.oop.ex11.a

top level class는 2가지로 통제할 수 있다.

public, (default)

접근 제어

com.eomcs.oop.ex11.a.sub.A.java
top level class 의 접근 제한자

🔹 package-private class (비공개 패키지 멤버 클래스)
(package-private) = (default)
현재 패키지 안에서만 쓰이고 다른 패키지에서 접근 불가
오로지 같은 패키지만 가능. 서브 패키지도 접근 불가.

com.eomcs.oop.ex11.a.Exam0110.java
top level class : 접근 범위

특정 클래스 안에서만 사용하는 00면 굳이 외부에 노출하지마라
작은 클래스인 경우만 말하는 거

⭐️ com.eomcs.oop.ex11.a.Exam0210.java
nested class : 종류

🔹 중첩 클래스를 만들 때 static을 붙일까 말까 고민하는가?
static을 붙이는 게 기본인데
언제 static을 안 붙여야 되는가 생각해야 됨

중첩 클래스 안에서 바깥 클래스 객체 주소가 필요하면
바깥 클래스의 인스턴스를 써야 되면

Exam0210 outer; {
  static class B {
    public B(Exam0210 outer) {
      this.outer = outer;
    }
    void m() {
      outer.b = 100;
    }
  }
}

내부적으로 바깥 클래스의 인스턴스 멤버를 접근하는 상황이라면
코딩을 좀 더 편하게 하기 위해서
직접 바깥에 인스턴스 주소를 받을 생성자를 만들 필요 없고
바깥 클래스의 인스턴스 주소를 보관할 필드를 만들 필요 없이
간단하게 자동으로 생성하게 하는 방법이 static을 떼는 거

🔹 중첩 클래스를 만들 때
1. 특정 클래스 안에서만 쓰이는 클래스가 있다면
그 클래스 안에 static으로 클래스를 선언하라

­2. 근데 그 안에 중첩되어 있는 클래스를 사용할 때
바깥 클래스의 인스턴스가 필요하다면
바깥 클래스의 인스턴스를 사용해야 되는 상황이라면
자동으로 바깥 클래스의 인스턴스를 주입할 수 있도록 static을 떼라

­3. 특정 메서드 안에서만 사용하는 중첩 클래스라면 로컬 클래스로 만들어라

­4. 인스턴스를 딱 한 개만 생성해서 사용할 거라면 익명 클래스로 만들어라

수퍼 클래스를 상속받은 익명 클래스를 정의한다.
수퍼 클래스의 생성자 호출
파라미터 값을 안 주면 기본 생성자를 호출하는 거고
파라미터를 주면 그거에 해당하는 생성자를 호출한다

수퍼 클래스 대신 인터페이스가 올 수 있다

수퍼 클래스를 상속받은 익명 클래스
인터페이스를 구현한 익명 클래스

수퍼 클래스를 지정하거나 인터페이스를 지정하거나 둘 중 하나만 해야 한다.

익명 클래스에서는 인터페이스 다중 상속 불가능!

이 자리에는 익명 클래스의 주소가 놓인다
익명 클래스의 주소를 받으려면 레퍼런스로 받아야 한다
클래스 obj = new 익명 클래스
인터페이스 obj = new 익명 클래스
익명 클래스가 상속받는 수퍼 클래스명이 오거나
익명 클래스가 구현한 인터페이스명만 가능
다형적 변수

컴파일러 입장에서는
Object에는 m1() 이라는 메서드가 없으므로 컴파일 에러
The method m1() is undefined for the type Object

((형변환클래스명)obj).m1();

익명 클래스는 새로운 메서드를 추가시키는 게 목적이 아님
이런 상황이면 익명 클래스를 사용하면 안 됨

com.eomcs.oop.ex11.a.Exam0310.java
nested class : 접근 제어

중첩 클래스도 클래스의 멤버이기 때문에
필드나 메서드처럼 접근 제한자를 붙일 수 있다.

원래 클래스 앞에는 접근 제어자가 (default)랑 public만 되는데
중첩 클래스에는 (default), public, private, protected 모두 가능

com.eomcs.oop.ex11.a.Exam0311.java
nested class : 로컬 클래스의 접근 제어

앞에 public 무슨 의미가 있냐
로컬 클래스도 로컬 변수랑 똑같이 접근 제어자를 붙일 수 없다.

com.eomcs.oop.ex11.a.sub.Exam0110.java
top level class : 접근 범위

(package-private) = (default)
public으로 공개되지 않은 클래스는 같은 패키지에서만 접근할 수 있다.

com.eomcs.oop.ex11.b

com.eomcs.oop.ex11.b.Exam0110.java
static nested class : 클래스 정의와 인스턴스 생성

스태틱 중첩 클래스는 다른 클래스에서 사용할 수 있다.

스태틱 멤버
스태틱 메서드 생각하면 됨
클래스명.메서드명() 이렇게 호출함
클래스명.변수명()

주는 아니지만 부가적인 용도
작은 클래스를 패키지로 관리하지 말고
하나의 클래스 안에 담아서 소스 코드 관리를 편하게 하기 위해서

안드로이드 OnClickListener ← 클래스 안에 중첩된 인터페이스명
맨 앞이 대문자
자바는 변수나 메서드는 소문자로 시작함

이벤트 처리 함수가 On으로 시작함
혹시 자바 개발자 중에서 메서드명을 대문자로 적는 사람들이 C++에서 건너온 사람들..
자바는 자바 스타일로...
파이썬은 파이썬 스타일로...
그 스타일을 따르는 게 가장 깔끔하고 세련된 방법

클래스 안에 중첩된 인터페이스명
인터페이스명은 보통
갑자기 전치사구 형태
View.OnClickListener
중첩 인터페이스임 (nested interface)

다른 데서도 필요한 거임
패키지로 구분하니까 관리하기 더 안 좋음
다른 쪽에서 쓰더라도 View
UI 컴포넌트의 수퍼 클래스 View
마우스 클릭 이벤트에 대한 규칙을 정의한 게 OnClickListener
View 클래스 안에서만 쓰이려고 한 게 아니라
View에 두는 게 더 직관적이고
패키지로 구분하는 것보다는 클래스에 종속시켜서 구분하는 게 더 유지보수 하는 게 좋다
관리 차원에서 좀 더 쉽게 관리하기 위해서 클래스 안에
안드로이드 예제가 태반임
바깥클래스명.중첩클래스명

import com.eomcs.oop.ex11.b.A.X;

클래스 안에 있는 중첩 클래스까지 import
마치 top level class인 것처럼 쓸 수 있다

com.eomcs.oop.ex11.b.Exam0210.java
static nested class : 다른 멤버에 접근하기

스태틱 메서드는 같은 스태틱 필드와 메서드를 쓸 수 있다.
같은 클래스에 있으면 클래스명 생략 가능

인스턴스 멤버
앞에 인스턴스 주소가 있어야 됨
앞에 자동으로 this가 붙는댔
스태틱 멤버는 this 라는 빌트-인 변수가 없기 때문에
스태틱 멤버는 this 라는 내장 변수가 없다
설사 스태틱 메서드 앞에 인스턴스 주소를 주더라도
아예 존재하지 않음

스태틱 멤버는 인스턴스 멤버를 사용할 수 없다
스태틱 멤버는 인스턴스 주소를 받지 못하니까 접근할 수 없다

인스턴스 메서드는 반드시 인스턴스 주소를 줘야 된다

java-lang/bin/main/com/eomcs/oop/ex11/b/B.class
인스턴스 멤버는
Local variable table에 this 라는 변수가 있다

스태틱 멤버는 다른 스태틱 멤버를 사용할 수 있다

eomcs-java-lang/app/build.gradle

com.eomcs.oop.ex11.b.Exam0310.java
static nested class : 다른 멤버가 중첩 클래스 사용하기

com.eomcs.oop.ex11.b.Exam0410.java
static nested class : import static 사용 전

컴파일러가 컴파일할 때 모든 클래스명을 다 적는다
패키지 이름을 무조건 다 적어야 됨
코딩을 편하게 하라고 만든 문법이 import 라는 문법
컴파일러가 붙임
어차피 컴파일 할 때

com.eomcs.oop.ex11.b.Exam0420.java
static nested class : import static 사용 후

import static 으로 미리 스태틱 멤버의 패키지 정보를 알려주면
마치 같은 클래스의 멤버인 것처럼 클래스 이름 없이 사용할 수 있다.

아예 중첩 클래스까지 import 할 수 있다

중첩 클래스를 import 할 때는 static을 적지 않는다.

static 메서드, 변수도 import 할 수 있다 (인스턴스 멤버는 안 됨)
static 메서드를 import 할 때는 static을 써줘야 함

아무리 찾아봐도 v1이 없다
수퍼 클래스 가봐도 없다
당황스럽다
그럴 때는 import를 찾아봐라

com.eomcs.oop.ex11.b.Exam0430.java
static nested class : import static 사용 후

// 각각의 스태틱 멤버를 지정하는 대신 
// 다음과 같이 wildcard(*)를 사용하여 전체 스태틱 멤버를 한 번에 지정할 수 있다.
import static com.eomcs.oop.ex11.b.F.*;
import static com.eomcs.oop.ex11.b.sub.M.*;
import com.eomcs.oop.ex11.b.F.X;
import com.eomcs.oop.ex11.b.sub.M.Y;

저장할 때 다 import 되니까
Ctrl + Z 하고 다시 저장하기

각각의 스태틱 멤버를 지정하는 게
다른 개발자가 소스 코드를 이해하는 데 더 도움이 된다

com.eomcs.oop.ex11.c

com.eomcs.oop.ex11.c.Exam0110.java
inner class : 클래스 정의와 인스턴스 생성

스태틱 중첩 클래스를 inner class 라고 부르지 마라
논-스태틱 중첩 클래스만 inner class 라고 부른다

컴파일러는 inner class를 컴파일 할 때 다음과 같이
‐ 바깥 클래스의 인스턴스 주소를 저장할 필드를 추가하고,
‐ 클래스의 인스턴스 주소를 파라미터로 받는 생성자를 만든다.

중첩 클래스를 만들 때 일단 static을 붙인다
static을 뗄지 말지 이게 체크 사항
static을 뗀다는 건 바깥 클래스의 인스턴스를 쓴다는 거
바깥 클래스를 받는 필드와 생성자를 자동으로 만들겠다는 거
즉 중첩 클래스에서 바깥 클래스의 인스턴스를 사용할 일이 있다면 static을 떼라

class X에 static 붙여 보기

java-lang/bin/main/com/eomcs/oop/ex11/c/X.class

X$A.class

컴파일러가 기본 생성자를 자동으로 추가한다
첫 번째 줄에 수퍼 클래스의 생성자를 호출
인스턴스 주소를 받을 this

밑에 부가적인 정보가 있다
바깥 클래스 A의

static 다시 빼기

바깥 클래스의 객체 주소를 받는 생성자가 자동으로 만들어진다.

com.eomcs.oop.ex11.c.Exam0111.java
inner class : 선언할 수 있는 멤버

inner class는 스태틱 멤버를 가질 수 없다.
The field v1 cannot be declared static in a non-static inner type, unless initialized with a constant expression

스태틱 멤버는 오직 top level class나 static nested class만 가질 수 있다.

jdk 15까지는 에런데 16부터 풀림

현업에 가면 17 안 씀

자바 컴파일러 옵션
하위 버전으로 맞춰서 컴파일 할 수 있음
자바 17버전을 깔면 11버전으로 동작되게 컴파일할 수 있다
항상 자바는 상위 버전은 하위 버전을 호환되게

gradle로 컴파일 할 때 옵션

inner class 객체를 생성할 때는 반드시 바깥 클래스의 인스턴스 주소를 써야 한다.

바깥 클래스의 인스턴스를 먼저 만든 다음에
inner class의 인스턴스를 만든다.

com.eomcs.oop.ex11.c.Exam0210.java
inner class : 바깥 클래스의 스태틱 멤버에 접근하기

생략 가능하지만 그냥 바깥 클래스 이름 적기
생략된 거니까 당황하지 말기

com.eomcs.oop.ex11.c.Exam0220.java
inner class : 바깥 클래스의 인스턴스 멤버 접근하기

바깥 객체의 인스턴스 멤버에 접근하려면,
inner 객체에 보관된 바깥 객체 주소를 사용해야 한다.

문제는 컴파일러가 자동 생성한 필드 이름이 뭔지 모른다.

그래서 자바는 inner 객체에 보관된 바깥 객체를 가리키는 다음의 문법을 제공하고 있다.
바깥클래스명.this
위의 문법을 이용하여 바깥 객체에 접근할 수 있다.

com.eomcs.oop.ex11.c.Exam0230.java
inner class : 바깥 클래스의 인스턴스 멤버 접근하기 II

inner class의 객체

03-OOP2 / 40 페이지

인스턴스에는 인스턴스 필드와 인스턴스의 클래스 정보만 있다

중첩 클래스 안 들어 있음

클래스 코드는 Method Area 영역에 있다

outer.new X();

인스턴스 필드만 들어감
메서드 안 들어감

inner class에는 하나 더 있음
this$0 ← 바깥 클래스의 주소가 들어있다

앞에 this 라는
메서드를 호출한 인스턴스의 주소가 들어 있다.
this에는 300번지가 들어 있다.

v1
가장 가까운 v1을 찾는다

B3.this.v1 // B3.this → this$0

같은 바깥 클래스 주소를 가리킨다

inner class의 객체를 만들 때 어떤 바깥 클래스의 객체를 사용하느냐에 따라서 달라진다

com.eomcs.oop.ex11.c.Exam0240.java

로컬 변수 이름과 인스턴스 변수 이름과 바깥 객체의 인스턴스 변수 이름이 겹치지 않으면 바깥클래스명.this 생략 가능하다

com.eomcs.oop.ex11.c.Exam0310.java
inner class : 다른 멤버가 중첩 클래스 사용하기

항상 inner class는 바깥 클래스의 인스턴스 주소가 있어야 인스턴스 생성 가능

식이 성립하기 위한 전제 조건이 있음

호출됐다는 전제 조건 C 인스턴스
이미 this에 인스턴스 주소가 들어 있다는 전제가 성립
인스턴스 메서드 안에 있는 코드를 살펴볼 때는 m2가 호출됐다는 가정이 전제
반드시 인스턴스 주소가 있기 때문에 호출된 거임
전제 조건을 가정하고 프로그래밍 짜기
m2 라는 메서드가 호출되었을 때
C 클래스의 인스턴스를 만들고 메서드를 호출했겠지
C 클래스의 주소를 가지고 호출했다는 거
C outer = null;
유효한 인스턴스가 없으면 실행 자체가 안 됨
유효한 주소를 넘길 때만 정상적으로 실행할 수 있다

  void test() {
    m2();  // 앞에 this가 생략된 거
  }

자기가 받은 this로 호출하는 거
생략이 된 거지 this 없이 호출이 안 됨
앞에 this가 생략된 건데 인스턴스 주소 없이 생략이 되네??? 라고 생각해버림
인스턴스 메서드 test()가 호출됐다는 것은 인스턴스 주소가 넘어왔다는 것이고
this 라는 변수에 인스턴스 변수가 들어 있고
자기가 받은 주소를 또 넘기는 거. 같은 주소.
인스턴스 메서드, 인스턴스 필드는 인스턴스 주소 없이 절대 사용 불가.

inner class의 객체를 만들 때는 바깥 클래스의 주소가 필요하다
X 클래스도 C 클래스의 멤버
X obj = this.new X();
인스턴스 메서드에서는 inner class의 객체를 마음대로 만들 수 있다.
→ this가 생략된 거

static 메서드에는 this 변수가 존재하지 않으므로
인스턴스 주소를 갖고 있지 않다
static 블럭에서 inner class 객체를 만들 수 없다

com.eomcs.oop.ex11.c.Exam0410.java

com.eomcs.oop.ex11.c.Exam0520.java
X(F arg0) {}
이런 식으로 바뀌었더라도 저렇게 호출은 못 함

com.eomcs.oop.ex11.c.Exam0610.java

ineer class는 컴파일 할 때 this$0 필드 생성
기본 생성자의 바깥 클래스 객체 주소를 받을 파라미터를 추가할 것이고
기존의 int 값을 받는 기존 생성자 앞부분에 바깥 클래스 변수를 담을 파라미터를 추가할 것이다
바깥 객체의 주소를 받는 생성자를 생성할 것이고
this$0 = arg0;
this$0로 바깥 클래스에 접근하면 된다
바깥클래스명.this
바깥 클래스 객체의 인스턴스 메서드나 필드에 접근할 수 있다

com.eomcs.oop.ex11.c.Exam0620.java

this를 명시하지 않았을 때 변수를 찾는 순서
1. 로컬 변수
2. 인스턴스 필드
3. 바깥 클래스의 인스턴스 필드

com.eomcs.oop.ex11.c.Exam0711.java
inner class 응용 I : 1단계 - 스태틱 중첩 클래스 사용

com.eomcs.oop.ex11.c.Exam0712.java
inner class 응용 I : 2단계 - 논스태틱 중첩 클래스(inner class) 사용

Musics2.Player p1 = m1.new Player();
생성자 호출 문장 앞쪽에 놓은 게 파라미터로 넘어간다.

com.eomcs.oop.ex11.c.Exam0713.java

객체 생성을 Musics3에게 맡긴다

음악 정보를 갖고 있는 Musics3

Gof의 팩토리 메서드

GRASP 설계 기법에서 "정보를 가진자가 그 일을 하라.(Information Expert)"를 적용

GRASP(General Responsibility Assignment Software Patterns)

roll = responsibility

넷플릭스 ------ 디즈니
정보가 가진 자(디즈니)가 직접 한다
디즈니 플러스

com.eomcs.oop.ex11.c.Exam0720.java
inner class 응용 II : inner 클래스와 인터페이스

인터페이스 구현체를 inner 클래스로 정의한다.

나중에 확장성을 생각해서 player 사용법을 아예 인터페이스로 따로 정의한 후
player 규칙에 따라서 동작하는 클래스를 만든 후에
그 클래스의 인스턴스를 반환한다.
이게 향후 시스템을 확장하기 좋은 구조다
확장성이 좋은... 자꾸만 클래스나 인터페이스가 늘어난다
자꾸만 뭔가 추가됨
유지보수가 좋은 구조
역할이 잘게 쪼개져 있고 얽혀져 있음
어떻게 연결되어 있는지 어떻게 돌아가는지 전체적인 상황을 정신 없음
페이지 컨트롤러로 끝냄

팩토리 메서드

com.eomcs.oop.ex11.d

com.eomcs.oop.ex11.d.Exam0110.java
local class : 클래스 정의와 인스턴스 생성

이 클래스는 이 메서드 안에서만 쓰인다는 거

$ ← 로컬 클래스구나

com.eomcs.oop.ex11.d.Exam0210.java

인스턴스 메서드 안에 있는 로컬 클래스에서는 바깥 클래스 객체 주소에 접근할 수 있다.

X obj = new X(); // 컴파일 ==> new X(this); 

com.eomcs.oop.ex11.d.Exam0220.java

스태틱 메서드는 C2 인스턴스 주소를 저장할 this라는 변수가 없다.
스태틱 메서드 안에 있는 로컬 클래스는 바깥 클래스 객체 주소에 접근할 수 없다.

X obj = new X(); // 컴파일 해도 같다. ===> new X();

스태틱 메서드 안에 선언됐느냐
인스턴스 메서드 안에 선언됐느냐에 따라서

com.eomcs.oop.ex11.d.Exam0310.java
local class에서 바깥 메서드의 로컬 변수 접근

자바스크립크 클로저랑 똑같은 동작 원리

컴파일이 끝나면 따로 존재한다

interest

바깥쪽 함수의 로컬 변수가 사라지기 전에 클로저 메모리에 따로 백업해둔다고 한 거랑 똑같음

컴파일러가 값을 저장해둘 필드와 생성자

float interestClone

return money + (money * interest);

컴파일 하게 되면 이렇게 바뀐다

public CalculatorImpl(float f) {

}

return money + (money * interestClone);

return new CalculatorImpl(interestClone);

java-lang/bin/main/com/eomcs/oop/ex11/d/CalculatorFactory$1CalculatorImpl.class

java-lang/bin/main/com/eomcs/oop/ex11/d/CalculatorFactory.class

자바스크립트 클로저랑 비슷
복제해둔다

com.eomcs.oop.ex11.d.Exam0321.java

값을 여러 번 할당한 경우에는 접근할 수 없다.

로컬 객체가 사용하는 로컬 변수는
메서드 호출이 끝났을 때 제거되기 때문이다.

340 넘어가기

com.eomcs.oop.ex11.e

인터페이스 기본이 static이며 생략 가능
인터페이스는 static으로 선언하든 안 하든 상관없음

인터페이스의 경우 static으로 선언하지 않아도 스태틱 멤버에서 사용할 수 있다.

호출하는 생성자는 수퍼 클래스의 생성자이다.
인터페이스는 생성자가 없음

    new A() {
      @Override
      public void print() {
        System.out.println("Hello!");
      }
    }.print();

자바의 모든 클래스는 따로 수퍼 클래스를 지정하지 않으면
java.lang.Object 클래스가 수퍼 클래스로 자동 설정된다.
Object 클래스의 생성자는 기본 생성자 하나 뿐이다.

com.eomcs.oop.ex11.e.Exam0120.java

익명 클래스에는 그런 문법은 없다.

com.eomcs.oop.ex11.e.Exam0210.java

인스턴스 초기화 블록

java-lang/bin/main/com/eomcs/oop/ex11/e/Exam0210$X.class

모든 생성자에 공통으로 들어가는 코드가 있다면 초기화 블럭에 집어 넣어라

  class X {

    public X() {
      System.out.println("Hello!");
    }

    public X(int a) {
      System.out.println("Hello!");
    }

    public X(String s) {
      System.out.println("Hello!");
    }

    {
      System.out.println("ABC#");
    }

  }

인스턴스 블럭에 들어 있는 코드는 결국
생성자의 앞부분에 들어 간다.
그래서 인스턴스 블럭에 있는 게 먼저 실행 된다는 말이 나옴

com.eomcs.oop.ex11.e.Exam0450.java

함수 인터페이스 (functional interface) : 메서드 1개 짜리 인터페이스

  static A create3() {
    return () ->System.out.println("Hello2!");
  }

리턴값이 인터페이스 구현체

메서드 레퍼런스

추상 메서드가 1개인 인터페이스 가능
디폴트 메서드는 있어도 상관없음
추상 메서드가 2개면 적용 안 됨

람다 문법, 메서드 레퍼런스는 오직 인터페이스만 가능

com.eomcs.oop.ex11.g

문자열을 상수로 정의한다.

잘못된 값을 넣을 수 없다!

그래서 실무에서는 보통 정수 값으로 처리한다.
이때 상수의 이름을 대문자로 지어서 구분하기 쉽도록 한다.

static 멤버 import
또는 다음과 같이 wildcard를 사용해도 된다.

상수를 그룹 별로 별도의 클래스로 분리하여 다루기

자잘한 클래스들을 별도의 파일로 분리하여 패키지 멤버 클래스로 정의하니까
클래스가 여러 개 생겨서 관리하기가 번거롭다.

일부러 소문자로 지음

OGNL(Object Graph Navigation Language)

자잘한 클래스를 묶을 때

특히 상수 값을 묶을 때 사용한다

안드로이드 R.java
이 기법을 이미 쓰고 있다

h는 주말에 훑어보기
i도 주말에 훑어보기

다시 한 번 overview 하면서 훑어보면 감이 올 거

12 람다 + 메서드 레퍼런스

다음주 월요일 DB

스레드 개념은 알고 있으니까 일단 DB 나가겠음

SQL 배우고 mylist에 적용

0개의 댓글