Android에서 열거 형 사용을 엄격히 피해야합니까?


93

Bundle다음과 같은 인터페이스에서 키와 같은 관련 상수 세트를 함께 정의했습니다 .

public interface From{
    String LOGIN_SCREEN = "LoginSCreen";
    String NOTIFICATION = "Notification";
    String WIDGET = "widget";
}

이것은 관련 상수를 함께 그룹화하고 정적 가져 오기 (구현이 아님)를 만들어 사용하는 더 좋은 방법을 제공합니다. 내가 알고있는 Android프레임 워크도 같은 동일한 방법으로 상수를 사용합니다 Toast.LENTH_LONG, View.GONE.

그러나 나는 종종 Java Enums상수를 나타내는 훨씬 더 좋고 강력한 방법을 제공 한다고 느낍니다 .

그러나 사용의 성취도 문제가 enums에가 Android?

약간의 조사로 나는 혼란에 빠졌습니다. 이 질문에서 ? 안드로이드의 성능 정보에서 제거 "당신 만의 int 필요 않도록 열거 형" 는 것이 분명 Google제거했습니다 "피 열거 형을" 성능 팁에서, 그러나 그것의에서 공식 훈련 문서는 메모리 오버 헤드에주의 가 명확하게 말한다 절 : "열거 형을 종종 정적 상수보다 두 배 이상의 메모리가 필요합니다. Android에서 열거 형을 사용하는 것은 엄격히 피해야합니다. " 여전히 유효합니까? (예 : Java1.6 이후 버전)

내가 관찰 한 가지 더 문제가 전송하는 것입니다 enums통해 intents사용하여 Bundle직렬화에 의해 내가 그들을 보내야합니다 (예 :putSerializable() , 기본 putString()메소드에 비해 비용이 많이 드는 작업이라고 생각하지만 enums무료로 제공하지만).

누군가 같은 것을 표현하는 가장 좋은 방법이 무엇인지 명확히 해줄 수 있습니까 Android? 에 사용 enums을 엄격히 피해야 Android합니까?


5
사용 가능한 도구를 사용해야합니다. 실제로 활동 또는 조각은 많은 메모리와 CPU 사용량을 차지하지만 사용을 중단 할 이유가 없습니다. 필요한 경우 static int를 사용하고 필요할 때 enum을 사용하십시오.
Patrick

11
나는 동의한다. 이것은 조기 최적화처럼 보입니다. 성능 및 / 또는 메모리 문제가 있고 프로파일 링을 통해 열거 형이 원인임을 증명할 수있는 경우가 아니면 의미가있는 곳에 사용하십시오.
GreyBeardedGeek

2
예전에는 열거 형으로 인해 트라이 벌이 아닌 성능 저하가 발생했다고 믿어졌지만 최근 벤치 마크에서는 대신 상수를 사용하는 것에 대한 이점이 없습니다. 참조 stackoverflow.com/questions/24491160/... 뿐만 아니라 stackoverflow.com/questions/5143256/...
벤자민 중사

1
번들에서 열거 형을 직렬화하는 성능 저하를 방지하려면 Enum.ordinal()대신 사용하여 int로 전달할 수 있습니다 .
BladeCoder 2015

1
마침내 여기 열거 성능 문제에 대한 몇 가지 설명이있다 youtube.com/watch?v=Hzs6OBcvNQE
nvinayshetty

답변:


116

enum기능이 필요할 때 사용하십시오 . 엄격하게 피하지 마십시오 .

Java enum은 더 강력하지만 기능이 필요하지 않은 경우 상수를 사용하면 공간을 덜 차지하고 원시 자체가 될 수 있습니다.

열거 형을 사용하는 경우 :

  • 당신이 받아 들일 수 - 유형 검사 (내가 부르는 아래 참조 목록에 값을, 그들은 연속없는 연속 여기)
  • 메서드 오버로딩-모든 열거 형 상수에는 자체 메서드 구현이 있습니다.

    public enum UnitConverter{
        METERS{
            @Override
            public double toMiles(final double meters){
                return meters * 0.00062137D;
            }
    
            @Override
            public double toMeters(final double meters){
                return meters;
            }
        },
        MILES{
            @Override
            public double toMiles(final double miles){
                return miles;
            }
    
            @Override
            public double toMeters(final double miles){
                return miles / 0.00062137D;
            }
        };
    
        public abstract double toMiles(double unit);
        public abstract double toMeters(double unit);
    }
  • 더 많은 데이터-하나의 상수에 하나의 변수에 넣을 수없는 하나 이상의 정보가 포함되어 있습니다.

  • 복잡한 데이터-데이터를 조작하는 데 필요한 방법

하면 되지 열거를 사용하는 방법 :

  • 한 유형의 모든 값을 허용 할 수 있으며 상수에는 가장 많이 사용되는 값만 포함됩니다.
  • 연속 데이터를받을 수 있습니다.

    public class Month{
        public static final int JANUARY = 1;
        public static final int FEBRUARY = 2;
        public static final int MARCH = 3;
        ...
    
        public static String getName(final int month){
            if(month <= 0 || month > 12){
                throw new IllegalArgumentException("Invalid month number: " + month);
            }
    
            ...
        }
    }
  • 이름 (예를 들어)
  • 실제로 열거 형이 필요하지 않은 다른 모든 것

열거 형은 더 많은 공간을 차지합니다.

  • 열거 형 상수에 대한 단일 참조는 4 바이트를 차지 합니다.
  • 모든 열거 형 상수는 8 바이트에 정렬 된 필드 크기 + 개체의 오버 헤드의 합계 인 공간을 차지 합니다.
  • 열거 형 클래스 자체가 일부 공간을 차지합니다.

상수는 더 적은 공간을 차지합니다.

  • 상수에는 참조가 없으므로 순수한 데이터입니다 (참조 인 경우에도 열거 형 인스턴스는 다른 참조에 대한 참조가 됨).
  • 기존 클래스에 상수를 추가 할 수 있습니다. 다른 클래스를 추가 할 필요가 없습니다.
  • 상수는 인라인 될 수 있습니다. 확장 된 컴파일 타임 기능을 제공합니다 (예 : null 검사, 데드 코드 찾기 등).

2
@IntDef 주석을 사용하여 int 상수에 대한 유형 검사를 시뮬레이션 할 수 있습니다. Java Enum을 선호하는 인수가 하나 적습니다.
BladeCoder 2015

3
더 @BladeCoder, 당신은 상수에 대한 참조가 필요 없어
카밀 Jarosz

1
또한 열거 형을 사용하면 리플렉션 사용이 촉진된다는 점에 유의하는 것이 좋습니다. 리플렉션을 사용하면 Android의 성능이 크게 저하되는 것으로 알려져 있습니다. 여기를 참조하십시오 : blog.nimbledroid.com/2016/02/23/slow-Android-reflection.html
w3bshark

3
@ w3bshark 열거 형은 리플렉션 사용을 어떻게 촉진합니까?
Kevin Krumwiede

3
명심해야 할 또 다른 사항은 표준 빈 "Hello, world!"의 힙 덤프입니다. 프로젝트에는 4,000 개 이상의 클래스와 700,000 개 이상의 개체가 포함됩니다. 수천 개의 상수가있는 우스꽝스럽고 거대한 열거 형이 있더라도 Android 프레임 워크 자체의 부풀음 옆에 성능 효과가 무시할 수있을 것이라고 확신 할 수 있습니다.
Kevin Krumwiede

57

열거 형에 단순히 값이있는 경우 다음과 같이 IntDef / StringDef를 사용해야합니다.

https://developer.android.com/studio/write/annotations.html#enum-annotations

예 : 대신 :

enum NavigationMode {NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS} 

너는 사용한다:

@IntDef({NAVIGATION_MODE_STANDARD, NAVIGATION_MODE_LIST, NAVIGATION_MODE_TABS})
@Retention(RetentionPolicy.SOURCE)
public @interface NavigationMode {}

public static final int NAVIGATION_MODE_STANDARD = 0;
public static final int NAVIGATION_MODE_LIST = 1;
public static final int NAVIGATION_MODE_TABS = 2;

매개 변수 / 반환 된 값으로있는 함수에서 다음을 사용하십시오.

@NavigationMode
public abstract int getNavigationMode();

public abstract void setNavigationMode(@NavigationMode int mode);

열거 형이 복잡한 경우 열거 형을 사용하십시오. 그렇게 나쁘진 않아.

열거 형과 상수 값을 비교하려면 여기를 읽어야합니다.

http://hsc.com/Blog/Best-Practices-For-Memory-Optimization-on-Android-1

그들의 예는 2 개의 값이있는 열거 형입니다. 상수 정수를 사용할 때 128 바이트에 비해 dex 파일에서 1112 바이트를 사용합니다. 열거 형이 실제 클래스이므로 C / C ++에서 작동하는 방식과는 대조적으로 의미가 있습니다.


열거 형의 장단점을 제공하는 다른 답변에 감사하지만, 특히 Android에 대한 최상의 솔루션을 제공 하므로이 답변이 허용되는 답변이어야합니다. 지원 주석을 사용하면됩니다. 우리는 Android 개발자가 "나중에 메모리 / 성능에 대해 걱정하기 시작하지 않는 한 다른 답변의 사고 방식보다는"열거 형을 사용할 강력한 이유가없는 한, 주석과 함께 상수를 사용할 것 "이라고 생각해야합니다. 열거 형을 사용할 수 있습니다. " 나중에 성능 문제가 발생하지 않도록하십시오!
w3bshark

3
@ w3bshark 성능 문제가있는 경우 열거 형은 문제를 해결하기 위해 가장 먼저 생각해야 할 일이 아닙니다. 주석에는 고유 한 절충점이 있습니다. 컴파일러 지원이없고, 고유 한 필드와 메서드를 가질 수없고, 작성하는 데 지루하며, int에 주석을 추가하는 것을 쉽게 잊을 수 있습니다. 전반적으로 더 나은 솔루션은 아니며 필요할 때 힙 공간을 절약하기 위해 있습니다.
Malcolm

1
@androiddeveloper enum 선언에 정의되지 않은 상수를 전달하여 실수를 할 수 없습니다. 이 코드는 컴파일되지 않습니다. IntDef주석 과 함께 잘못된 상수를 전달하면 그럴 것입니다. 시계에 관해서도 꽤 분명하다고 생각합니다. CPU 전력, RAM, 배터리가 더 많은 기기는 무엇인가요? 휴대 전화 또는 시계? 그리고 그 때문에 성능을 위해 더 최적화하기 위해 소프트웨어가 필요한 것은 무엇입니까?
Malcolm

3
@androiddeveloper 좋습니다. 엄격한 용어를 고집한다면 실수로 컴파일 타임 오류 (런타임 또는 프로그램 논리 오류)가 아닌 오류를 의미했습니다. 나를 트롤링하고 있습니까? 아니면 컴파일 타임 오류의 이점과 차이점을 이해하지 못합니까? 이것은 잘 논의 된 주제입니다. 웹에서 찾아보십시오. 시계와 관련하여 Android에는 더 많은 기기가 포함되어 있지만 가장 제한적인 기기는 가장 낮은 공통 분모가됩니다.
Malcolm

2
@androiddeveloper 나는 이미 두 진술에 모두 응답했으며, 한 번 더 반복해도 내가 말한 내용이 무효화되지 않습니다.
Malcolm

12

이전 답변 외에도 Proguard를 사용하는 경우 (확실히 크기를 줄이고 코드를 난독 화하기 위해 수행해야 함) 가능한 모든 곳 Enums으로 자동 변환됩니다 @IntDef.

https://www.guardsquare.com/en/proguard/manual/optimizations

클래스 / 언 박싱 / 열거 형

가능한 경우 열거 형 유형을 정수 상수로 단순화합니다.

따라서 일부 불연속 값이 있고 일부 메서드가 동일한 유형의 다른 값이 아닌이 값만 사용할 수 있도록 허용해야하는 경우 EnumProguard가이 수동 코드 최적화 작업을 수행 할 것이기 때문에를 사용 합니다.

그리고 여기에 Jake Wharton의 열거 형 사용에 대한 좋은 게시물이 있습니다.

라이브러리 개발자로서 저는 소비하는 앱의 크기, 메모리 및 성능에 가능한 한 영향을 미치지 않기 위해 수행해야하는 이러한 작은 최적화를 인식합니다. 그러나 [...] 공용 API에 열거 형 대 정수 값을 적절한 곳에 두는 것이 완벽하다는 사실을 인식하는 것이 중요합니다. 정보에 입각 한 결정을 내리는 차이를 아는 것이 중요합니다.


11

Android에서 열거 형 사용을 엄격히 피해야합니까?

아니요. " 엄격하게 "는 너무 나빠서 전혀 사용해서는 안된다는 의미입니다. 열거 형 (ui 스레드에서 연속)을 사용하는 많은 (수천 또는 수백만) 작업과 같은 극단적 인 상황에서 성능 문제가 발생할 수 있습니다 . 훨씬 더 일반적인 것은 백그라운드 스레드에서 엄격하게 발생 해야하는 네트워크 I / O 작업입니다 . 열거의 가장 일반적인 사용은 아마 유형 검사의 일종입니다 - 객체인지 확인 또는 그것을 너무 빨리 당신이 열거 형의 단일 비교 및 정수의 비교 사이의 차이를 발견 할 수 없습니다이다.

누군가가 Android에서 동일한 것을 나타내는 가장 좋은 방법을 명확히 할 수 있습니까?

이것에 대한 일반적인 경험 법칙은 없습니다. 자신에게 맞는 것을 사용하고 앱을 준비하는 데 도움이됩니다. 나중에 최적화-앱의 일부 측면을 느리게하는 병목 현상이 있음을 확인한 후.


1
지원 주석과 함께 상수를 사용하기 쉽고 지금 최적화하는 것이 나중에 최적화하는 이유는 무엇입니까? 여기에서 @android_developer의 답변을 참조하십시오.
w3bshark

11

Android P에서 Google은 열거 형 사용에 제한 / 이의가 없습니다.

문서는 이전에주의하도록 권장되었던 곳에서 변경되었지만 지금은 언급하지 않습니다. https://developer.android.com/reference/java/lang/Enum


1
이것 외에 다른 증거가 있습니까? 나는 그것에 대해 @JakeWharton 진술을 찾았습니다 : twitter.com/jakewharton/status/1067790191237181441 . 누구나 바이트 코드를 확인할 수 있습니다.
soshial

4

두 가지 사실.

1, Enum은 JAVA에서 가장 강력한 기능 중 하나입니다.

2, Android 휴대 전화에는 일반적으로 많은 메모리가 있습니다.

그래서 제 대답은 아니오입니다. Android에서 Enum을 사용하겠습니다.


2
Android에 메모리가 많다고해서 앱이 모든 메모리를 소비하고 다른 사람에게는 아무것도 남겨 두지 않아야한다는 의미는 아닙니다. Android OS에 의해 앱이 죽지 않도록하려면 모범 사례를 따라야합니다. 나는 열거 형을 사용하지 말고 대안이 없을 때만 사용하십시오.
Varundroid

1
모범 사례를 따라야한다는 데 동의하지만 모범 사례가 항상 최소한의 메모리를 사용하는 것은 아닙니다. 코드를 깨끗하고 이해하기 쉽게 유지하는 것이 몇 k 메모리를 절약하는 것보다 훨씬 더 중요합니다. 균형을 찾아야합니다.
Kai Wang

1
균형을 찾아야한다는 데 동의하지만 TypeDef를 사용한다고해서 코드가 나쁘게 보이거나 유지 관리가 어려워지는 것은 아닙니다. 나는 지금 1 년 이상 그것을 사용하고 있으며 내 코드가 이해하기 쉽지 않다고 느끼지 않았으며, 동료 중 누구도 TypeDef가 열거 형에 비해 이해하기 어렵다고 불평하지 않았습니다. 내 조언은 다른 옵션이 없을 때만 enum을 사용하고 가능하면 피하는 것입니다.
Varundroid

2

추가하고 싶습니다. 키 또는 값이 주석 인터페이스 중 하나 인 List <> 또는 Map <>을 선언 할 때 @Annotations를 사용할 수 없습니다. "여기에는 주석을 사용할 수 없습니다."라는 오류가 표시됩니다.

enum Values { One, Two, Three }
Map<String, Values> myMap;    // This works

// ... but ...
public static final int ONE = 1;
public static final int TWO = 2;
public static final int THREE = 3;

@Retention(RetentionPolicy.SOURCE)
@IntDef({ONE, TWO, THREE})
public @interface Values {}

Map<String, @Values Integer> myMap;    // *** ERROR ***

따라서 목록 / 맵으로 압축해야 할 때 enum을 사용하십시오. 추가 할 수 있지만 @annotated int / string 그룹은 할 수 없습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.