Kotlin의 상수-권장되는 방법은 무엇입니까?


165

Kotlin에서 상수를 만드는 것이 어떻게 권장됩니까? 명명 규칙은 무엇입니까? 설명서에서 찾지 못했습니다.

companion object {
    //1
    val MY_CONST = "something"

    //2
    const val MY_CONST = "something"

    //3
    val myConst = "something"
}

아니면 ...?


4
public static finalJava 의 필드에 해당하는 것을 원하면 const val컴패니언 객체에서 사용 하십시오. private static final필드와 공개 게터 를 원하면 val컴패니언 객체에 사용 하십시오.
— Michael

2
Kotlin에서 상수를 정의하는 방법을 설명하는 블로그 게시물은 다음과 같습니다. blog.egorand.me/where-do-i-put-my-constants-in-kotlin
— Micer

체크 아웃 이 기사를 . 관련 성능 트레이드 오프와 함께 상수를 저장할 수있는 다양한 방법에 대한 개요를 제공합니다.
— firedrillsergeant

답변:


132

Kotlin에서 클래스에서 사용할 로컬 상수를 만들려면 아래와 같이 만들 수 있습니다

val MY_CONSTANT = "Constants"

그리고 자바의 public static final 과 같이 kotlin에 public 상수를 만들려면 다음과 같이 만들 수 있습니다.

companion object{

     const val MY_CONSTANT = "Constants"

}

3
이름이 지정된 새 파일과 같은 별도의 파일에서 Constants.kt어떻게 사용합니까?
— Naveed Abbas

2
상수에 파일을 사용합니다. 거기에 내 모든 상수를 유지하십시오.
— filthy_wizard

2
companion object@piotrpo의 답변이 받아 들여 져야한다고 생각 하지 않아도됩니다.
— Chiara

@Chiara 동반 객체 (및 해당 클래스)는 최상위 수준 선언과 달리 네임 스페이스 역할을합니다. 상황에 따라 두 가지 대답이 모두 합리적이라고 생각합니다.
— jingx

@ jingx 예, 네임 스페이스를 추가 할 필요가 있습니다. : +1 :
— Chiara

118

컴패니언 개체를 사용하지 마십시오. 후드 뒤에는 필드에 액세스 할 수 있도록 getter 및 setter 인스턴스 메소드가 작성됩니다. 인스턴스 메소드 호출은 정적 메소드 호출보다 기술적으로 비쌉니다.

public class DbConstants {
    companion object {
        val TABLE_USER_ATTRIBUTE_EMPID = "_id"
        val TABLE_USER_ATTRIBUTE_DATA = "data"
    }

대신에 상수를 정의하십시오 object.

권장 연습 :

object DbConstants {
        const val TABLE_USER_ATTRIBUTE_EMPID = "_id"
        const val TABLE_USER_ATTRIBUTE_DATA = "data"
}

다음과 같이 전 세계에 액세스하십시오. DbConstants.TABLE_USER_ATTRIBUTE_EMPID


컴패니언 객체는 특별한 경우가 아닌가? const val컴패니언 오브젝트 const val의 일반 오브젝트와 다른 오브젝트가 될 수있는 방법 (예를 들어 const, 컴패니언 오브젝트의 경우 생략 한 것 같습니다. 추가 const하면 예제의 성능이 동일해야 함)
— Erwin Bolwidt

1
@ErwinBolwidt @sudesh의 요점은 구조의 유일한 목적이 일부 상수 값에 대한 네임 스페이스를 제공하는 것이라면 클래스 랩핑 동반 객체 디자인을 사용해서는 안된다는 것입니다. 그러나 구조가 인스턴스화 가능해야하고 몇 개의을 포함해야하는 경우 a를 const val선언하는 companion object것이 맞습니다.
— Ari Lacenski

7
@ ErwinBolwidt : sudesh가 맞습니다. 동반자 객체에 대해 생성 된 바이트 코드는 후드 아래에서 getter를 사용하여 추가 객체를 생성합니다. 디 컴파일 코 틀린 예제와 좋은 설명을 참조 blog.egorand.me/where-do-i-put-my-constants-in-kotlin
— 도미니크

2
@dominik 덕분에,이, 나는 깊이 이것을 이해하고자하는 모든이를 아주 상세한 기사 추천되고, 코 틀린는 차선의 바이트 코드를 생성하는 경우가 많다, JetBrains의은을 감시 ... 많은 같은 성능 관련 버그를 해결 한 논의 .kotlinlang.org 에는 이러한 여러 기본 측면에 대한 정보가 제공됩니다.
— sudesh

1
나는 오늘 당신의 대답에서 많은 것을 배웠습니다 @sudesh thanks!
— Rakhi Dhavale

34

우선 상수에 대한 Kotlin의 명명 규칙은 java와 동일합니다 (예 : MY_CONST_IN_UPPERCASE).

어떻게 만들어야합니까?

1. 최상위 수준으로 (권장)

클래스 선언 외부에서 const를 배치해야합니다 .

두 가지 가능성 : 클래스 파일에 const를 선언하십시오 (const는 클래스와 명확한 관계가 있습니다)

private const val CONST_USED_BY_MY_CLASS = 1

class MyClass { 
    // I can use my const in my class body 
}

전역 const를 저장할 전용 constants.kt 파일을 만듭니다 (여기에서 프로젝트 전체에서 const를 광범위하게 사용하려는 경우).

package com.project.constants
const val URL_PATH = "https:/"

그런 다음 필요한 곳으로 가져와야합니다.

import com.project.constants

MyClass {
    private fun foo() {
        val url = URL_PATH
        System.out.print(url) // https://
    }
}

2. 컴패니언 객체 (또는 객체 선언)에서 선언

바이트 코드가 생성 될 때 쓸모없는 객체가 만들어지기 때문에 이것은 훨씬 덜 깨끗합니다.

MyClass {
    companion object {
        private const val URL_PATH = "https://"
        const val PUBLIC_URL_PATH = "https://public" // Accessible in other project files via MyClass.PUBLIC_URL_PATH
    }
}

const 대신 val로 선언하면 더 나빠 집니다 (컴파일러는 쓸모없는 객체 + 쓸모없는 함수를 생성합니다) :

MyClass {
    companion object {
        val URL_PATH = "https://"
    }
}

노트 :

kotlin에서 const는 기본 유형을 보유 할 수 있습니다. 함수를 전달하려면 @JvmField 주석을 추가해야합니다. 컴파일 타임에는 공개 정적 최종 변수로 변환됩니다. 그러나 기본 유형보다 느립니다. 피하십시오.

@JvmField val foo = Foo()

이것이 정답이어야합니다. 어쨌든 다음과 같은 경우 : public static final Pattern REGEX_NOTEMPTY = Pattern.compile ( ". +") ????
— Xan

23

컴파일 타임에 알려진 값은 상수로 표시 될 수 있습니다.

이름 지정 규칙은 Java 규칙을 따라야하며 Java 코드에서 사용될 때 올바르게 표시되어야합니다 (어쨌든 컴패니언 객체로는 달성하기 어렵습니다).

적절한 상수 선언은 다음과 같습니다.

const val MY_CONST = "something"
const val MY_INT = 1

3
Naming conventions should follow Java ones- 왜?
— Jodimoro

3
Kotlin은 일반적으로 다르게 지정되지 않은 경우 interop을 매끄럽게 만들기 위해 기본적으로 Java 규칙을 따릅니다.
— zsmb13


2
@ 닐, 그렇지 않습니다.
— Jodimoro

13
그 링크에서 나는 그들이 말한 것을 게시했다If in doubt, default to the Java Coding Conventions
— Neil

16

Kotlin에서 상수를 선언하기 위해 클래스, 객체 또는 동반 객체가 필요하지 않습니다. 모든 상수를 보유한 파일을 선언하고 (예 : Constants.kt 또는 기존 Kotlin 파일에 넣을 수 있음) 파일 내부에 상수를 직접 선언 할 수 있습니다. 컴파일 타임에 알려진 상수는로 표시되어야합니다 const.

따라서이 경우 다음과 같아야합니다.

const val MY_CONST = "something"

다음을 사용하여 상수를 가져올 수 있습니다.

import package_name.MY_CONST

이 링크를 참조 할 수 있습니다


13
상수는 관련된 클래스에 있어야합니다. 'Constants'클래스를 만들면 결국 그 안에 수백 개의 상수가 끝납니다. Pe : MAX_WIDTH, MAX_HEIGHT는 Screen 클래스에 있어야 논리적으로 접근 할 수 있습니다 : Screen.MAX_WIDTH 그리고 Constants.SCREEN_MAX_WIDTH를 넣을 필요가 없습니다.이 상수는 NOBODY로 인해 2 년 동안 Constants.SCR_MAX_W 및 Constants.MAX_WIDTH와 중복됩니다. Ctrl + 스페이스를 눌러 자동 완성 할 때 수백 / 수천 줄을 아래로 스크롤합니다. 진지하게 :하지 마십시오. 유지 보수
— 불가능

1
@inigoD 한 곳에서 또는 어린이에서만 상수를 사용하는 경우에 해당하지만 사실은 거의 없습니다. 불분명 한 클래스에 상수를 넣으면 코드베이스를 인수 할 가능성을 잊어 버릴 수 있습니다. 또는 어디에 넣을 지 확실하지 않습니다. 소스 또는 대상? 찾기 쉬운 여러 상수 파일을 만들 수 있습니다. 하나는 환경 설정 키, 하나는 요청 키, 하나는 뷰 상수 등입니다.
— Herrbert74

1
@ Herrbert74 죄송하지만 동의하지 않습니다. 때로는 어떤 것이 있는지 찾기가 어려울 수 있지만 항상 일정한 장소는 항상 더 관련이있는 클래스 여야한다는 데 동의합니다. 나중에 무작위로 파일을 무작위로 저장하는 것이 가장 좋은 방법은 아닙니다 ... 나중에 무작위로 저장하지 않고 패키지에 상수가 관련되어 있다고 주장 할 것입니다. 그들이 관련된 수업에 넣지
— 마라

4
상수가 실제로 전역이거나 모든 패키지에서 사용되는 주석 값 또는 여러 컨트롤러에서 가져 오는 헤더 이름과 같이 넓은 범위를 갖는 경우 "상수를 만드는 것이 허용됩니다. 적절하게 범위가 지정된 클래스 " 그러나 특정 컨텍스트에서만 사용되는 상수는 해당 컨텍스트의 범위를 지정하고 관련 클래스에서 선언해야합니다.
— Nephthys76

@ Nephthys76 참고로, " 모든 패키지에서 사용되는 주석의 값 " 과 같이 특히 상수의 가장 좋은 장소는 주석 클래스에 있다고합니다.
— 양배추

8

const val valName = valValue클래스 이름 앞에을 넣으면 이런 식으로

public static final YourClass.Kt그 public static final값 을 가질 것 입니다.

코 틀린 :

const val MY_CONST0 = 0
const val MY_CONST1 = 1
data class MyClass(var some: String)

자바 디 컴파일 :

public final class MyClassKt {
    public static final int MY_CONST0 = 0;
    public static final int MY_CONST1 = 1;
}
// rest of MyClass.java

이것이 사실입니까? 이 방법에 대한 경험이 있습니까?
— Scott Biggs

5
class Myclass {

 companion object {
        const val MYCONSTANT = 479
}

const키워드 를 사용 하거나 @JvmFieldJava를 정적 최종 상수로 만드는 두 가지 선택 사항이 있습니다 .

class Myclass {

     companion object {
           @JvmField val MYCONSTANT = 479
    }

@JvmField주석을 사용하면 주석이 컴파일 된 후 상수가 Java에서 호출하는 방식으로 입력됩니다.
자바에서 호출하는 것처럼 코드에서 컴패니언 상수를 호출하면 컴파일러가이를 대체합니다.

그러나 const 키워드를 사용하면 상수 값이 인라인됩니다. 인라인 i는 실제 값이 컴파일 된 후 사용됨을 의미합니다.

그래서 여기에 컴파일러가 당신을 위해 할 일이 요약되어 있습니다 :

//so for @JvmField:

Foo var1 = Constants.FOO;

//and for const:

Foo var1 = 479

5

코 틀린 정적 및 상수 값 및 메소드 선언

object MyConstant {

@JvmField   // for access in java code 
val PI: Double = 3.14

@JvmStatic // JvmStatic annotation for access in java code
fun sumValue(v1: Int, v2: Int): Int {
    return v1 + v2
}

}

어디서나 액세스 가치

val value = MyConstant.PI
val value = MyConstant.sumValue(10,5)

1
전역 또는 정적 방법을 정의하는 방법?
— Samad Talukder

@SamadTalukder Kotlin에서는 재미있을 것입니다 sumValue (v1 : Int, v2 : Int) : Int {return v1 + v2}
— Shomu

5

마찬가지로 키워드로 val정의 된 변수 const는 변경할 수 없습니다. 여기서 차이점은 const컴파일 타임에 알려진 변수에 사용 된다는 것 입니다.

변수를 선언하는 const것은 staticJava 에서 키워드를 사용하는 것과 매우 유사합니다 .

코 틀린에서 const 변수를 선언하는 방법을 보자 :

const val COMMUNITY_NAME = "wiki"

Java로 작성된 유사한 코드는 다음과 같습니다.

final static String COMMUNITY_NAME = "wiki";

위의 답변에 추가-

@JvmField Kotlin 컴파일러에게이 속성에 대한 getter / setter를 생성하지 않고 필드로 노출하지 않도록 지시하는 데 사용됩니다.

 @JvmField
 val COMMUNITY_NAME: "Wiki"

정적 필드

명명 된 객체 또는 컴패니언 객체에 선언 된 Kotlin 속성에는 해당 명명 된 객체 또는 컴패니언 객체를 포함하는 클래스에 정적 백업 필드가 있습니다.

일반적으로 이러한 필드는 비공개이지만 다음 방법 중 하나로 노출 될 수 있습니다.

  • @JvmField 주석;
  • lateinit 개질제;
  • const 수정 자.

자세한 내용은 여기-https: //kotlinlang.org/docs/reference/java-to-kotlin-interop.html#instance-fields


4

답변에 언급되지 않은 것은을 사용하는 오버 헤드입니다 companion objects. 여기서 읽을 수 있듯이 컴패니언 객체는 실제로 객체이며 객체를 만들면 리소스가 소비됩니다. 또한 상수를 사용할 때마다 둘 이상의 게터 함수를 거쳐야 할 수도 있습니다. 필요한 것이 소수의 기본 상수라면 성능 을 높이고를 피하기 위해 사용 val하는 것이 좋습니다companion object .

TL; DR; 기사의 :

컴패니언 객체를 사용하면 실제로이 코드가됩니다.

class MyClass {

    companion object {
        private val TAG = "TAG"
    }

    fun helloWorld() {
        println(TAG)
    }
}

이 코드로 :

public final class MyClass {
    private static final String TAG = "TAG";
    public static final Companion companion = new Companion();

    // synthetic
    public static final String access$getTAG$cp() {
        return TAG;
    }

    public static final class Companion {
        private final String getTAG() {
            return MyClass.access$getTAG$cp();
        }

        // synthetic
        public static final String access$getTAG$p(Companion c) {
            return c.getTAG();
        }
    }

    public final void helloWorld() {
        System.out.println(Companion.access$getTAG$p(companion));
    }
}

따라서 피하십시오.


3

지역 상수 :

const val NAME = "name"

전역 상수 :

object MyConstants{
    val NAME = "name"
    val ID = "_id"
    var EMAIL = "email"
}

MyConstants에 액세스하십시오 .NAME


1

Kotlin에서 상수를 정의 할 수있는 몇 가지 방법이 있습니다.

컴패니언 객체 사용

    companion object {
        const val ITEM1 = "item1"
        const val ITEM2 = "item2"
    }

모든 클래스 내에서 위의 컴패니언 객체 블록을 사용하고이 블록 자체 내의 모든 필드를 정의 할 수 있습니다. 그러나이 방법에는 문제가있다.

컴패니언 객체의 멤버는 다른 언어에서는 정적 멤버처럼 보이지만 런타임에 여전히 실제 객체의 인스턴스 멤버이며 인터페이스를 구현할 수 있습니다.

컴패니언 객체를 사용하여 상수를 만들고 디 컴파일 된 바이트 코드 를 보면 다음과 같은 결과가 나타납니다.

  ClassName.Companion Companion = ClassName.Companion.$$INSTANCE;
  @NotNull
  String ITEM1 = "item1";
  @NotNull
  String ITEM2 = "item2";

  public static final class Companion {
     @NotNull
     private static final String ITEM1 = "item1";
     @NotNull
     public static final String ITEM2 = "item2";

     // $FF: synthetic field
     static final ClassName.Companion $$INSTANCE;

     private Companion() {
     }

     static {
        ClassName.Companion var0 = new ClassName.Companion();
        $$INSTANCE = var0;
     }
  }

여기 에서 컴패니언 객체의 멤버가 다른 언어의 정적 멤버처럼 보이지만 런타임에 여전히 실제 객체의 인스턴스 멤버 인 경우에도 문서에서 말한 내용을 쉽게 확인할 수 있습니다 . 필요한 것보다 추가 작업을 수행하고 있습니다.

다음과 같이 컴패니언 객체를 사용할 필요가없는 다른 방법이 있습니다.

object ApiConstants {
      val ITEM1: String = "item1"
 }

위의 스 니펫 바이트 코드의 디 컴파일 된 버전을 보면 다음과 같이 보일 것입니다.

public final class ApiConstants {
     private static final String ITEM1 = "item1";

     public static final ApiConstants INSTANCE;

     public final String getITEM1() {
           return ITEM1;
      }

     private ApiConstants() {
      }

     static {
         ApiConstants var0 = new ApiConstants();
         INSTANCE = var0;
         CONNECT_TIMEOUT = "item1";
      }
    }

위의 디 컴파일 된 코드가 보이면 각 변수에 대해 get 메소드를 작성하는 것입니다. 이 get 메소드는 전혀 필요하지 않습니다.

이 get 메소드를 제거하려면 아래처럼 val 앞에 const 를 사용해야 합니다.

object ApiConstants {
     const val ITEM1: String = "item1"
 }

위의 스 니펫의 디 컴파일 된 코드가 표시되면 코드의 백그라운드 변환이 가장 적기 때문에 쉽게 읽을 수 있습니다.

public final class ApiConstants {
    public static final String ITEM1 = "item1";
    public static final ApiConstants INSTANCE;

    private ApiConstants() {
     }

    static {
        ApiConstants var0 = new ApiConstants();
        INSTANCE = var0;
      }
    }

상수를 만드는 가장 좋은 방법입니다.


0

프리미티브 및 문자열의 경우 :

/** The empty String. */
const val EMPTY_STRING = ""

다른 경우 :

/** The empty array of Strings. */
@JvmField val EMPTY_STRING_ARRAY = arrayOfNulls<String>(0)

예:

/*
 * Copyright 2018 Vorlonsoft LLC
 *
 * Licensed under The MIT License (MIT)
 */

package com.vorlonsoft.android.rate

import com.vorlonsoft.android.rate.Constants.Utils.Companion.UTILITY_CLASS_MESSAGE

/**
 * Constants Class - the constants class of the AndroidRate library.
 *
 * @constructor Constants is a utility class and it can't be instantiated.
 * @since       1.1.8
 * @version     1.2.1
 * @author      Alexander Savin
 */
internal class Constants private constructor() {
    /** Constants Class initializer block. */
    init {
        throw UnsupportedOperationException("Constants$UTILITY_CLASS_MESSAGE")
    }

    /**
     * Constants.Date Class - the date constants class of the AndroidRate library.
     *
     * @constructor Constants.Date is a utility class and it can't be instantiated.
     * @since       1.1.8
     * @version     1.2.1
     * @author      Alexander Savin
     */
    internal class Date private constructor() {
        /** Constants.Date Class initializer block. */
        init {
            throw UnsupportedOperationException("Constants.Date$UTILITY_CLASS_MESSAGE")
        }

        /** The singleton contains date constants. */
        companion object {
            /** The time unit representing one year in days. */
            const val YEAR_IN_DAYS = 365.toShort()
        }
    }

    /**
     * Constants.Utils Class - the utils constants class of the AndroidRate library.
     *
     * @constructor Constants.Utils is a utility class and it can't be instantiated.
     * @since       1.1.8
     * @version     1.2.1
     * @author      Alexander Savin
     */
    internal class Utils private constructor() {
        /** Constants.Utils Class initializer block. */
        init {
            throw UnsupportedOperationException("Constants.Utils$UTILITY_CLASS_MESSAGE")
        }

        /** The singleton contains utils constants. */
        companion object {
            /** The empty String. */
            const val EMPTY_STRING = ""
            /** The empty array of Strings. */
            @JvmField val EMPTY_STRING_ARRAY = arrayOfNulls<String>(0)
            /** The part 2 of a utility class unsupported operation exception message. */
            const val UTILITY_CLASS_MESSAGE = " is a utility class and it can't be instantiated!"
        }
    }
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책과 개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.