Skip to content

Latest commit

 

History

History
369 lines (243 loc) · 14.2 KB

Java (20) e886cef800df4636aeaa55fb81050023.md

File metadata and controls

369 lines (243 loc) · 14.2 KB

Java (20)

작업중: In progress 태그: Dev, 백엔드프로그래밍, 웹&모바일, 자바프로그래밍기초 학습일: 01/27/2023

중첩클래스 (nested class)

package com.eomcs.oop.ex11;

  • 다른 클래스 안에 정의된 클래스

  • 해당 클래스 안에서만 사용된다.

    ⇒ 특정 클래스 안에서만 사용되는 클래스가 있다면 중첩 클래스로 선언해라. 노출 범위를 좁히는 것이 유지보수에 좋다.

패키지 멤버 클래스 (top level class) 접근 범위

  • public 으로 공개된 클래스는 다른 패키지에서 접근 할 수 있다.
  • public 으로 공개되지 않은 클래스는 다른 패키지에서 접근할 수 없다.
  • (package-private; modifier 가 없다) : 다른 패키지 접근 불가!≠ public

따라서, 같은 패키지 내에 존재하거나, public 으로 공개된 클래스거나

중첩클래스 종류

  1. static nested class

    ⇒ 바깥 클래스의 인스턴스에 종속되지 않는 클래스.

    ⇒ top level class 와 동일하게 사용한다.

  2. non-static nested class = inner class (논스태틱중첩클래스 = 인스턴스중첩클래스)

    ⇒ 바깥 클래스의 인스턴스에 종속되는 클래스.

    중첩 클래스에서 바깥 클래스의 인스턴스 멤버를 사용한다.

    ⇒ 바깥 클래스의 인스턴스 없이 생성할 수 없다.

  3. local class

    ⇒ 특정 메서드 안에 정의되어 그 메서드 안에서만 사용되는 클래스

    ⇒ 객체 생성코드 + 클래스 정의

    접근 제한자를 붙일 수 없다.

  4. anonymous class (익명클래스)

    ⇒ 클래스 이름이 없기 때문에 생성자를 정의할 수 없고,

    컴파일러가 디폴트 생성자를 자동 생성한다. (수퍼클래스와 동일하게)

    (초기화를 원하면 "인스턴스 블록"을 사용)

    ⇒ 모든 클래스의 생성자는 항상 수퍼 클래스의 생성자를 첫 줄에 먼저 호출한다.

    ⇒ (클래스를 정의 하는 문법 + 인스턴스를 만드는 문법) 동시에 해야 한다.

    ⇒ 단 한 개의 인스턴스만 생성해서 사용할 경우 익명 클래스를 적용한다.

    • 문법

      **new** 수퍼클래스() {수퍼 클래스를 상속 받은 익명 클래스 정의}

      **new** 인터페이스() {인터페이스를 구현한 익명 클래스 정의}

      • 주의!

        new extends 수퍼클래스 implements 인터페이스 {익명 클래스 정의} ← 이런 문법은 없다.

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

    # 예제
    Object obj = new Object() {
          // Object 클래스를 상속 받은 익명 클래스를 만들고,
          // m1() 메서드를 추가한다.
          public void m1() {
            System.out.println("Hello!");
          }
        };

중첩클래스와 .class 파일의 관계

Untitled

cf. 컴파일 후에 중첩클래스는 모두 추출되어,

바깥클래스에 중첩클래스는 없다. > navigator 확인 가능 eg. A$X

Nest Members / Host 정보만 기재된다.

  • local class [바깥클래스명]$[정의된순서][로컬클래스명].class

    ⇒ 정의된 순서이지, 나열된 순서가 아니다.

문법

  • static

    • 클래스 정의와 인스턴스 생성

      바깥클래스명.중첩클래스명…

      1. 레퍼런스 선언
      2. 인스턴스 생성
    • 선언할 수 있는 멤버

      • 스태틱/논스태틱 모두 가능
    • 다른 멤버(필드, 메서드)에 접근

      • 바깥클래스 명으로 접근하며, 같은 클래스일 경우 생략 가능
      • 스태틱 멤버(스태틱 중첩 클래스가 포함)는 this 라는 빌트인 변수가 없어서, 인스턴스 멤버에 접근 할 수 없다.
    • 다른 멤버가 스태틱 중첩 클래스 사용하기

      • 스태틱/논스태틱 멤버 모두 사용 가능
    • 중첩클래스가 다른 패키지일 경우, import static 가능

      • import로 미리 알려주면, 마치 클래스 멤버인양 클래스명 없이 사용 가능
      • 중첩 클래스를 import 할 때는 static을 적지 않는다.
      • wildcard(*) 사용 가능
  • inner class

    • 클래스 정의와 인스턴스 생성

      1. 레퍼런스 선언

      바깥클래스명.중첩클래스명…

      1. 인스턴스 생성

      바깥 클래스의 인스턴스 주소 없이 생성 불가! 인스턴스 멤버를 사용하려면 반드시 해당 객체가 있어야 한다

      ⇒ 바깥 클래스 인스턴스 준비

      package com.eomcs.oop.ex11.c.0110;

      논스태틱중첩클래스는 바깥 클래스의 인스턴스 주소를 저장할 필드를 추가하거나, 생성자를 별도로 만들 필요가 없다.

      컴파일러 자동 생성

      eg. myapp > AbstractList의 Iterator() {}

    • 선언할 수 있는 멤버

      • inner class (논스태틱중첩클래스)는 스태틱 멤버를 가질 수 없다.

        → 단, Java16 부터는 inner class도 스태틱 멤버를 가질 수 있다.

        …………

  • local class

    • 인스턴스 메서드와 로컬 클래스

      • 인스턴스 메서드는 C 인스턴스 주소를 this 변수에 저장한다.

      그래서 인스턴스 메서드 안에 정의된 로컬 클래스는 바깥 클래스의 인스턴스를 사용할 수 있다.

    • 스태틱 메서드와 로컬 클래스

      • 스태틱 메서드는 C2 인스턴스 주소를 저장할 this라는 변수가 없다.

      그래서 바깥 클래스의 인스턴스를 사용할 수 없다.

      .class 파일을 확인해보면 바깥 클래스의 인스턴스를 보관할 필드가 선언되어 있지 않다.

      또한 생성자에도 바깥 클래스의 인스턴스를 받는 파라미터가 선언되어 있지 않다.

    • 로컬 클래스에서 사용하는 로컬 변수에 대한 제약 조건

      • final 또는 final 처럼 사용해야 한다.
  • anonymous class

    • 인터페이스를 구현한 익명 클래스 정의

      인터페이스의 경우 static으로 선언하지 않아도 스태틱 멤버에서 사용할 수 있다. => 인터페이스는 규칙을 정의한 것이기 때문에 인스턴스 멤버라는 개념이 존재하지 않는다.

    • 익명 클래스로 인터페이스 구현하기

      문법:

      인터페이스명 레퍼런스 = new 인터페이스명() {};

      • 파라미터 없이 호출하는 익명 클래스의 기본 생성자를 호출한다.
      • 파라미터를 가지고 생성자를 호출한다면, 그 파라미터를 받는 수퍼클래스의 생성자를 호출할 것이다.
      // 실무용
          new A() {
            @Override
            public void print() {
              System.out.println("Hello!");
            }
          }.print();
    • 익명 클래스도 클래스라, 기본 생성자가 생성된다.

      • 파라미터가 있는 수퍼 클래스를 상속 받는다면?

        ⇒ 익명 클래스의 객체를 만들 때 호출하는 생성자는

        수퍼 클래스에 존재하는 생성자여야 한다.

        = 컴파일러는 호출하는 수퍼클래스의 생성자와 동일한 파라미터를 갖는 생성자를, 익명 클래스에 추가한다.

    • 수퍼 클래스와 인터페이스를 동시에 지정할 수 없다

    • 여러 개의 인터페이스를 구현할 수 없다

    • 모든 클래스의 생성자는 항상 수퍼 클래스의 생성자를 첫 줄에 먼저 호출한다.

    • 익명 클래스가 놓이는 자리

      • 스태틱 필드의 값을 준비할 때 익명 클래스를 사용할 수 있다.

      • 인스턴스 필드의 값을 준비할 때 익명 클래스를 사용할 수 있다.

      • 로컬 변수의 값을 준비할 때 익명 클래스를 사용할 수 있다.

      • 파라미터 자리에 바로 삽입 가능하다. ⇒람다까지 응용 가능

        package com.eomcs.oop.ex11.e; 450

        test step4 : 람다문법 활용 : 메서드 1개짜리 인터페이스를 구현하는 경우 클래스 껍데기 지우고, 오버라이드 지우고 메서드명 지우고 화살표 문장이 한개면 중괄호, 세미콜론 생략 가능

cf. review

static nested class vs non-static nested class

스태틱중첩클래스

컴파일러가 기본 생성자 추가

인스턴스중첩클래스

컴파일러가 바깥 클래스의 객체 주소를 받는 레퍼런스

생성자에서 바깥 클래스의 인스턴스를 받을 수 있도록 파라미터와 저장 코드를 추가

바깥 클래스의 객체 주소를 넘기는 코드로 자동 변경


Lamda

package com.eomcs.oop.ex12;

411 중요

로컬(또는 익명 로컬) 클래스 안에서 바깥 메서드의 로컬 변수를 사용하면 컴파일러는 자동으로 그 값을 보관하기 위해 필드를 추가한다. 또한 그 값을 받을 수 있도록 생성자를 변경한다. 따라서 개발자가 직접 필드나 생성자를 정의할 필요가 없다.

중요!!!! 여기서 rate는 컴파일러가 생성한 필드를 가리킨다. 바깥 메서드의 파라미터가 아니다.

익명 클래스 vs 람다

추상 메서드가 한 개 있는 인터페이스를 "functional interface”

⇒ 여러 개의 메서드가 있다 하더라도 추상 메서드가 한 개이면 된다.

⇒ 인터페이스가 아닌 추상 클래스는 람다 구현의 대상이 아니다!

람다 문법으로 인터페이스 구현하기 ⇒ 메서드 한 개짜리 인터페이스를 좀 더 간결하게 구현하기 위해 만든 문법이다.

cf.

람다와 .class 파일

⇒ 람다는 별도의 .class 파일을 생성하지 않는다.

⇒ 컴파일러는 람다 코드를 해당 클래스의 스태틱 메서드로 정의한다.

⇒ 그리고 리플렉션 API를 사용하여 이 메서드를 호출하도록 변경한다.

⇒ 람다 문법이 초기에 등장했을 때는 익명 클래스로 변환되었다.

⇒ 그러나 최근에는 메서드로 변환한 후 호출하는 방식으로 바뀌었다.

문법

  • 한 문장일 때는 중괄호를 생략할 수 있다.

    • 물론 중괄호를 명확히 적어도 된다.
    • 파라미터가 없다고 괄호를 생략할 수는 없다.
  • 여러 문장을 실행하는 경우 블록 {}으로 감싸라! (블록을 생략할 수 없다.)

  • 파라미터가 있을 경우

    • 파라미터는 괄호() 안에 선언한다.
    • 파라미터 타입을 생략할 수 있다.
    • 파라미터가 한 개일 때는 괄호도 생략할 수 있다.
    • 파라미터가 여러 개일 때는 괄호를 생략할 수 없다.
  • 리턴문에 적용

    • 리턴 값은 return 명령을 사용하여 처리한다.

    • 한 문장으로 된 **표현식(=값을 리턴하는 한 문장의 코드)**인 경우 괄호 생략할 수 있다.

      ⇒ 문장은 문장인데 값을 리턴하는 문장을 '표현식(expression)' 이라 부른다.

      단 괄호를 생략할 때 return 키워드도 생략해야 한다. 있으면 컴파일 오류!

    cf. Math.max() 도 값을 리턴하니깐, 표현식으로 간주한다.

    cf. 값을 리턴해야 하는데 람다 문장에서 값을 리턴하지 않으면 컴파일 오류!

  • 익명 클래스를 사용할 수 있는 곳에는 모두 람다 사용 가능

    • 스태틱필드, 인스턴스필드, 로컬변수, 파라미터, 리턴값, 리턴문장 모두

메서드 레퍼런스

기존에 작성한 클래스의 스태틱 메서드를 재활용하기 ⇒ 인터페이스의 메서드 규격과 일치하는 메서드가 있다면, 그 메서드를 람다 구현체로 대체할 수 있다. ⇒ 규격? 메서드 시그너처 (메서드의 파라미터 타입/개수/순서, 리턴 타입) ⇒ 문법: 클래스명::메서드명

⇒ 시그너처가 다르면 불가능

📍 리턴타입이 다를 때, 형변환이 된다면 가능하다 0530 참고 int ⇒ double int ⇒ float …

int ≠> short

메서드 레퍼런스를 지정할 때 리턴 타입의 규칙:

  1. 같은 리턴 타입

  2. 암시적 형변환 가능한 타입

  3. auto-boxing 가능한 타입

  4. void

결론, 메서드 레퍼런스가 가리키는 실제 메서드를 호출한 후 그 메서드가 리턴한 값이 인터페이스에 정의된 메서드의 리턴 값으로 사용할 수 있다면 문제가 없다.

메서드 레퍼런스를 지정할 때 파라미터 타입 규칙: => 인터페이스 규칙에 따라 받은 값을 실제 메서드에 그대로 전달할 수 있다면 가능하다.


24. List가 특정 타입의 목록만 다루도록 맞춤 설정하기 : 제네릭 문법 적용

Iterator next() 꺼내는 값 타입 고정하기 / List 와 나머지 또한

List 인터페이스를 구현하는 시점에서 다룰 데이터의 타입을 지정한다. 예) List

<제네릭>의 존재 이유

<타입이름을저장하는변수> : 타입 레퍼런스