Companion object 를 쓸 줄 안다 고요?아마 아니 야!

Kotlin 을 처음 접 했 을 때 이것 이 야 말로 진정한 OOP 언어 라 고 생각 했 습 니 다. 기본 유형 도 똑 같 습 니 다.나중에 자바 에서 정적 멤버 로 편리 하 게 하 는 장면 을 만 났 습 니 다. 완전한 OOP 는 저 를 어 쩔 수 없 게 만 들 었 습 니 다. 그래서 저 는 (Companion object) 동반 대상 을 찾 았 습 니 다.
사용 방법 은 대략 다음 과 같다.
class Main private constructor(){
    private var id: Int? = null   
    companion object {
        var previousId = -1    
        fun newInstance(): Main {
            val instance = Main()
            instance.id = previousId++
        }
    }

    fun main(args: Array) {
        val main = Main.newInstance()
        print((Main.previousId)
    }
}

이것 은 공장 방법 입 니 다. 사용 해 보면 자바 의 정적 구성원 과 매우 비슷 합 니 다. 그러나 우 리 는 이 필드 들 이 사실은 다른 대상 의 구성원 이라는 것 을 기억 해 야 합 니 다.컴 파일 러 가 또 몰래 우 리 를 도와 뭔 가 를 해 주 었 다)
언뜻 보기 에는 별 문제 가 없 는 것 같 습 니 다. 우리 자바 코드 도 이렇게 쓰 여 있 습 니 다. 독자 들 이 저 에 게 물 어 볼 것 같 습 니 다. 왜 반생 대상 만 알 고 있 으 면 안 되 는 지 이 정도 의 용법 이 아 닙 니까?
서 두 르 지 마라, 우리 가 바이트 번 호 를 벗 겨 보 자.
 // access flags 0x8
  static ()V
    NEW Main$Companion
    DUP
    ACONST_NULL
    INVOKESPECIAL Main$Companion. (Lkotlin/jvm/internal/DefaultConstructorMarker;)V
    PUTSTATIC Main.Companion : LMain$Companion;
   L0
    LINENUMBER 5 L0
    ICONST_M1
    PUTSTATIC Main.previousId : I
    RETURN
    MAXSTACK = 3
    MAXLOCALS = 0


우 리 는 이 부분 을 보 았 습 니 다. Main 류 가 불 러 올 때 Main $Companion 류 의 대상 을 만 들 었 습 니 다. 이것 은 동반 대상 이 확실히 대상 이 고 우 리 는 주요 정적 구성원 으로 사용 하 는 구성원 들 이 모두 이 대상 의 구성원 임 을 증명 합 니 다.
그러면 컴 파일 러 가 우리 에 게 만들어 준 이런 종류의 바이트 코드 를 살 펴 보 자.
 // access flags 0x2
  private ()V
   L0
    LINENUMBER 4 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object. ()V
    RETURN
   L1
    LOCALVARIABLE this LMain$Companion; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1001
  public synthetic (Lkotlin/jvm/internal/DefaultConstructorMarker;)V
   L0
    LINENUMBER 4 L0
    ALOAD 0
    INVOKESPECIAL Main$Companion. ()V
    RETURN
   L1
    LOCALVARIABLE this LMain$Companion; L0 L1 0
    LOCALVARIABLE $constructor_marker Lkotlin/jvm/internal/DefaultConstructorMarker; L0 L1 1
    MAXSTACK = 1
    MAXLOCALS = 2

우 리 는 기본 적 인 구조 함 수 를 제외 하고 컴 파일 러 가 그것 에 새로운 구조 함 수 를 합성 한 것 을 볼 수 있다.
또한 get, set 방법 으로 previousId 필드 를 방문 하여 대상 구성원 에 게 get, set 함 수 를 생 성 하 는 것 도 정상 입 니 다.

  // access flags 0x11
  public final getPreviousId()I
   L0
    LINENUMBER 5 L0
    INVOKESTATIC Main.access$getPreviousId$cp ()I
    IRETURN
   L1
    LOCALVARIABLE this LMain$Companion; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x11
  public final setPreviousId(I)V
    // annotable parameter count: 1 (visible)
    // annotable parameter count: 1 (invisible)
   L0
    LINENUMBER 5 L0
    ILOAD 1
    INVOKESTATIC Main.access$setPreviousId$cp (I)V
    RETURN
   L1
    LOCALVARIABLE this LMain$Companion; L0 L1 0
    LOCALVARIABLE  I L0 L1 1
    MAXSTACK = 1
    MAXLOCALS = 2

잠깐 만!왜 INVOKESTATIC 지령 이 있어!나 는 어떻게 또 Main 의 정적 방법 을 사용 하 는 지 눈 여 겨 보 았 다. 고 개 를 돌려 Main 의 바이트 코드 를 보 았 다. 과연 이런 방법 이 있 었 다.
 // access flags 0x1019
  public final static synthetic access$getPreviousId$cp()I
   L0
    LINENUMBER 1 L0
    GETSTATIC Main.previousId : I
    IRETURN
   L1
    MAXSTACK = 1
    MAXLOCALS = 0

  // access flags 0x1019
  public final static synthetic access$setPreviousId$cp(I)V
   L0
    LINENUMBER 1 L0
    ILOAD 0
    PUTSTATIC Main.previousId : I
    RETURN
   L1
    LOCALVARIABLE  I L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

아 쉽 습 니 다. OOP 를 한 번 조작 하면 여러 가지 방법 이 호출 되 었 습 니 다. 마지막 으로 Main 에 정적 구성원 을 만 들 었 고 id 에 접근 하 는 방법 도 생 성 했 습 니 다.
// access flags 0x1019
  public final static synthetic access$getId$p(LMain;)Ljava/lang/Integer;
   L0
    LINENUMBER 1 L0
    ALOAD 0
    GETFIELD Main.id : Ljava/lang/Integer;
    ARETURN
   L1
    LOCALVARIABLE $this LMain; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1019
  public final static synthetic access$setId$p(LMain;Ljava/lang/Integer;)V
   L0
    LINENUMBER 1 L0
    ALOAD 0
    ALOAD 1
    PUTFIELD Main.id : Ljava/lang/Integer;
    RETURN
   L1
    LOCALVARIABLE $this LMain; L0 L1 0
    LOCALVARIABLE  Ljava/lang/Integer; L0 L1 1
    MAXSTACK = 2
    MAXLOCALS = 2

나 는 기본 적 인 공장 방법 을 실현 하고 싶 은 데, 나 에 게 이렇게 많은 방법 을 만들어 줄 필요 가 있 습 니까?저 는 가만히 있 지 못 할 것 입 니 다. 저 는 또 장난 을 치기 시 작 했 습 니 다. previousId 는 정적 인 구성원 입 니 다. 그러면 이 를 진정한 정적 인 구성원 으로 만 들 방법 을 생각 하 세 요. new Instance 방법 은 원래 정적 인 대상 을 만 드 는 방법 이기 도 합 니 다.
@file:JvmName("Main")

@JvmField 
var previousId = -1  
 
class Main private constructor() {
    private var id: Int? = null   
    
    companion object {

        @JvmStatic
 fun newInstance(): Main {
            val instance = Main()
            instance.id = previousId++
        }
    }

    fun main(args: Array) {
        val main = Main.newInstance()
        print((previousId)
    }
}

나 는 이전에 이미 여러분 과 최상 위 멤버 들 의 협조 @JvmField 의 효과 에 대해 토론 한 적 이 있 습 니 다. @ file: JvmName 은 컴 파일 러 의 모든 최상 위 멤버 들 에 게 Main 과 같은 종류 에 넣 으 라 고 알 렸 습 니 다. 우 리 는 컴 파일 러 가 우리 에 게 그렇게 많은 추가 방법 을 만 드 는 비용 을 더 이상 받 지 않 아 도 됩 니 다. @JvmStatic 컴 파일 러 가 new Instance 방법 을 정적 인 방법 으로 직접 컴 파일 하 게 할 것 입 니 다.
이 때 생 성 된 바이트 코드 를 보십시오:
 // access flags 0x1001
  public synthetic (Lkotlin/jvm/internal/DefaultConstructorMarker;)V
   L0
    LINENUMBER 9 L0
    ALOAD 0
    INVOKESPECIAL Main$Companion. ()V
    RETURN
   L1
    LOCALVARIABLE this LMain$Companion; L0 L1 0
    LOCALVARIABLE $constructor_marker Lkotlin/jvm/internal/DefaultConstructorMarker; L0 L1 1
    MAXSTACK = 1
    MAXLOCALS = 2

Main $Companion 류 라 는 생 성 된 구조 함 수 를 제외 하고 컴 파일 러 는 우리 에 게 구 부 러 진 방법 을 만 들 지 않 을 것 입 니 다. 완벽 합 니 다!
우리 가 정 리 를 해 보 자. 사실은 동반 대상 에서 구성원 변 수 를 정의 하 는 것 을 피하 고 파일 에서 최상 위 변 수 를 정의 하 는 것 이다. 또한 동반 대상 중의 함 수 를 모두 @JvmStatic 로 수식 하여 진정한 정적 함수 로 만 들 수 있다.
다음 에 우 리 는 다시 Kotlin 의 독특한 유형 Range 을 벗 겨 냅 니 다.
다음으로 전송:https://juejin.im/post/5c9b8a595188251d093d14e5

좋은 웹페이지 즐겨찾기