这个功能会被放入以后的Java版本中吗?
有人能解释一下,为什么我不能像Java的switch
语句那样做吗?
这个功能会被放入以后的Java版本中吗?
有人能解释一下,为什么我不能像Java的switch
语句那样做吗?
Java SE 7已经实现了具有String
情况的开关语句,至少在16年后它们被首次请求。没有提供延迟的明确原因,但很可能与性能有关。
现在,该功能已经在javac
通过一个“去糖化”过程实现;使用case
声明中的String
常量的干净、高级语法在编译时扩展为遵循模式的更复杂的代码。生成的代码使用了一直存在的JVM指令。
具有String
情况的switch
在编译期间被转换为两个开关。第一个将每个字符串映射到唯一的整数——它在原始开关中的位置。这是通过首先切换标签的哈希码来完成的。相应的情况是一个测试字符串相等的if
语句;如果哈希上有冲突,则测试是一个级联的if-else-if
。第二个开关反映了原始源代码中的开关,但用其对应的位置替换了情况标签。这个两步过程使得保留原始开关的流程控制变得容易。
switch
的技术细节,请参考JVM规范,其中描述了switch语句的编译。简而言之,根据case中所使用常量的稀疏程度,有两种不同的JVM指令可用于switch。两者都依赖于为每个case使用整数常量以实现高效执行。tableswitch
指令。lookupswitch
指令。String
对象进行switch
时,很可能会同时使用这两个指令。 lookupswitch
适用于第一个基于哈希码的switch,以查找case的原始位置。结果的序数非常适合于tableswitch
。
这两个指令都要求在编译时对分配给每个case的整数常量进行排序。在运行时,虽然tableswitch
的O(1)
性能通常比lookupswitch
的O(log(n))
性能更好,但需要一些分析来确定表是否足够密集以证明空间-时间权衡。Bill Venners撰写了一篇很棒的文章,更详细地介绍了这一点,以及其他Java流程控制指令的底层实现。
JDK 7之前,enum
可以近似于基于String
的switch语句。这使用编译器在每个enum
类型上生成的静态的valueOf
方法。例如:
Pill p = Pill.valueOf(str);
switch(p) {
case RED: pop(); break;
case BLUE: push(); break;
}
ValueEnum enumval = ValueEnum.fromString(myString);
switch (enumval) {
case MILK: lap(); break;
case WATER: sip(); break;
case BEER: quaff(); break;
case OTHER:
default: dance(); break;
}
以下是一个完整的示例,基于JeeBee的帖子,使用Java枚举代替使用自定义方法。
请注意,在Java SE 7及更高版本中,您可以在switch语句的表达式中使用String对象。
public class Main {
/**
* @param args the command line arguments
*/
public static void main(String[] args) {
String current = args[0];
Days currentDay = Days.valueOf(current.toUpperCase());
switch (currentDay) {
case MONDAY:
case TUESDAY:
case WEDNESDAY:
System.out.println("boring");
break;
case THURSDAY:
System.out.println("getting better");
case FRIDAY:
case SATURDAY:
case SUNDAY:
System.out.println("much better");
break;
}
}
public enum Days {
MONDAY,
TUESDAY,
WEDNESDAY,
THURSDAY,
FRIDAY,
SATURDAY,
SUNDAY
}
}
自1.7以来,直接使用String
的示例也可以展示如下:
public static void main(String[] args) {
switch (args[0]) {
case "Monday":
case "Tuesday":
case "Wednesday":
System.out.println("boring");
break;
case "Thursday":
System.out.println("getting better");
case "Friday":
case "Saturday":
case "Sunday":
System.out.println("much better");
break;
}
}
詹姆斯·柯伦简洁地说:“基于整数的开关可以优化为非常高效的代码。基于其他数据类型的开关只能编译成一系列if()语句。出于这个原因,C和C ++只允许在整数类型上进行开关操作,因为在其他类型上进行开关操作是没有意义的。”
我的观点就是,一旦你开始在非基本类型上进行开关操作,你就需要开始考虑“equals”与“==”。首先,比较两个字符串可能会是一个相当冗长的过程,这会增加上述性能问题。其次,如果有使用字符串开关,则会要求忽略大小写、考虑/忽略位置环境、基于regex进行开关等。我赞同一项决定,即以节省语言开发者大量时间为代价,为程序员节约一小部分时间。
switch
看作是Java过去(回到C语言时代)面向过程的遗留物,认为它已经过时了。switch
仍然有其用处,至少因为它的速度很快。而且,它总比我在某些代码中看到过的一系列级联数字else if
要好...hashCode()
进行模拟。因为String.hashCode()
通常为不同字符串返回不同的值,对于相等字符串始终返回相等的值,所以它是相当可靠的(如@Lii在评论中提到的,不同的字符串可能会产生相同的哈希码,例如"FB"
和"Ea"
)。请参见文档。String s = "<Your String>";
switch(s.hashCode()) {
case "Hello".hashCode(): break;
case "Goodbye".hashCode(): break;
}
这样,你就可以在技术上切换一个 int
。
或者,你可以使用以下代码:
public final class Switch<T> {
private final HashMap<T, Runnable> cases = new HashMap<T, Runnable>(0);
public void addCase(T object, Runnable action) {
this.cases.put(object, action);
}
public void SWITCH(T object) {
for (T t : this.cases.keySet()) {
if (object.equals(t)) { // This means that the class works with any object!
this.cases.get(t).run();
break;
}
}
}
}
case
语句必须始终是常数值,而String.hashCode()
并不是常数值(即使在实践中它的计算结果从未在 JVM 之间改变)。 - StaxMancase
语句的值不必在编译时确定,因此它可以很好地工作。 - hyper-neutrino其他答案说这是在Java 7中添加的,并提供了早期版本的解决方法。本答案尝试回答“为什么”
Java是对C++过于复杂的反应,它被设计成一种简单清晰的语言。
String在语言中得到了一些特殊处理,但我认为设计者们试图将特殊情况和语法糖的数量保持最少。
在底层上,switch语句中的字符串切换相当复杂,因为字符串不是简单的原始类型。在Java设计时,这不是一个常见功能,也与极简主义的设计格格不入。特别是因为他们决定不为字符串特例化==,如果case在这里起作用,那么(现在)会有些奇怪。
在1.0和1.4之间,语言本身基本保持不变。 Java的大部分增强功能都是在库方面进行的。
这一切都随着Java 5的到来而改变,语言得到了大幅度扩展。在版本7和8中,还有进一步的扩展。我预计这种态度的改变是由于C#的崛起所驱动的。
//#switch(target)
case "foo": code;
//#end
“不要抱有期待。”
哈哈,http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=1223179 - raffian