Java 我应该严格避免在Android上使用枚举吗?
我曾在如下界面中定义一组相关常量,如Java 我应该严格避免在Android上使用枚举吗?,java,android,enums,Java,Android,Enums,我曾在如下界面中定义一组相关常量,如捆绑键: public interface From{ String LOGIN_SCREEN = "LoginSCreen"; String NOTIFICATION = "Notification"; String WIDGET = "widget"; } 这为我提供了一种更好的方法,将相关常量分组在一起,并通过静态导入(而不是实现)使用它们。我知道Android框架也以同样的方式使用常量,比如Toast.LENTH_LONG,V
捆绑键:
public interface From{
String LOGIN_SCREEN = "LoginSCreen";
String NOTIFICATION = "Notification";
String WIDGET = "widget";
}
这为我提供了一种更好的方法,将相关常量分组在一起,并通过静态导入(而不是实现)使用它们。我知道Android框架也以同样的方式使用常量,比如Toast.LENTH_LONG
,View.GONE
然而,我经常觉得javaenum
提供了更好、更强大的方法来表示常量
但是在Android
上使用enums
是否存在性能问题
经过一番研究,我最终陷入了困惑。从这个问题
很明显,Google
已经将“避免枚举”从其性能提示中删除,但在其官方培训文档部分,它明确指出:“枚举通常需要两倍于静态常量的内存。你应该严格避免在Android上使用枚举。”这仍然有效吗?(在Java
1.6之后的版本中)
我观察到的另一个问题是,使用Bundle
跨意图发送enum
,我应该通过序列化发送它们(即putSerializable()
,我认为与基元putString()
方法相比,这是一个昂贵的操作,尽管enum
是免费提供的)
有人能澄清一下,在Android
中,哪一种方式是最好的表达方式吗?我是否应该严格避免在Android
上使用enums
我应该严格避免在Android上使用枚举吗
不,“严格”的意思是它们太差了,根本不应该被使用。在极端情况下可能会出现性能问题,例如使用枚举(ui线程上的连续)执行的许多(数千或数百万)操作。更常见的是网络I/O操作,这些操作应该严格在后台线程中进行。
枚举最常见的用法可能是某种类型的检查——无论对象是this还是this,它的速度如此之快,您都不会注意到单个枚举比较和整数比较之间的差异
有人能澄清一下,在Android中,哪一种方式是最好的表达方式吗
这方面没有一般的经验法则。使用任何适合你并帮助你准备好应用程序的工具。稍后优化-当您注意到有一个瓶颈会减慢应用程序的某些方面时。当您需要它的功能时,请使用enum
不要严格避免它
JavaEnum功能更强大,但如果您不需要它的特性,可以使用常量,它们占用的空间更少,而且它们本身也可以是原语
何时使用枚举:
- 类型检查-您只能接受列出的值,并且它们不是连续的(请参见下面我在这里称之为连续的内容)
- 方法重载-每个枚举常量都有自己的方法实现
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);
}
...
}
}
公共课月{
公共静态最终int一月=1;
公共静态最终int二月=2;
公共静态最终int三月=3;
...
公共静态字符串getName(最后一个整数月){
如果(第12个月){
抛出新的IllegalArgumentException(“无效月号:“+月”);
}
...
}
}
- 名称(如您的示例中所示)
- 对于真正不需要枚举的所有其他内容
枚举占用更多空间
- 对枚举常量的单个引用占用4个字节
- 每个枚举常量占用的空间是其字段大小之和,与对象的8字节+开销对齐
- 枚举类本身占用一些空间
常数占用更少的空间
- 常量没有引用,因此它是纯数据(即使它是引用,枚举实例也将是对另一引用的引用)
- 可以将常量添加到现有类中-无需添加其他类
- 常数可以内联;它带来了扩展的编译时特性(如空检查、查找死代码等)
如果枚举只有值,则应尝试使用IntDef/StringDef,如下所示:
示例:而不是:
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);
如果枚举很复杂,请使用枚举。没那么糟
要比较枚举与常量值,请阅读以下内容:
他们的示例是一个具有2个值的枚举。与使用常量整数时的128字节相比,dex文件需要1112字节。这很有意义,因为枚举是真实的类,而不是它在C/C++上的工作方式。我想补充一点,当您声明一个列表或映射时,其中键或值是其中一个注释接口时,不能使用@Annotations。
出现错误“此处不允许批注”
enum值{1,2,3}
Map myMap;//这很有效
// ... 但是
公共静态最终整数=1;
公共静态最终int 2=2;
公共静态最终int 3=3;
@保留(RetentionPolicy.SOURCE)
@IntDef({1,2,3})
公共@接口值{}
映射myMap;//***错误***
因此,当您需要将其打包到列表/映射中时,请使用enum,因为它们可以添加,但@annotated int/string组无法添加。除了前面的答案之外,我还想补充一点,如果您使用的是Proguard(您肯定应该这样做以减小大小并混淆代码),那么您的enum将
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 ***