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.Ktpublic 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.