此功能是否将在以后的Java版本中使用?
有人可以解释为什么我不能这样做吗,就像Java switch
语句的技术方法那样?
"Don't hold your breath."
哈哈,bugs.sun.com
此功能是否将在以后的Java版本中使用?
有人可以解释为什么我不能这样做吗,就像Java switch
语句的技术方法那样?
"Don't hold your breath."
哈哈,bugs.sun.com
Answers:
带有String
案例的switch语句已在Java SE 7中实现,至少在首次提出要求后的 16年。没有提供延迟的明确原因,但可能与性能有关。
现在,该功能已javac
通过“脱糖”过程实现。String
在case
声明时使用常量的干净,高级语法在编译时扩展为遵循模式的更复杂的代码。生成的代码使用始终存在的JVM指令。
switch
带有String
case的A 在编译期间转换为两个开关。第一个将每个字符串映射到一个唯一的整数-它在原始开关中的位置。这是通过首先打开标签的哈希码来完成的。相应的情况是if
测试字符串是否相等的语句;如果哈希上有冲突,则测试为级联if-else-if
。第二个开关在原始源代码中进行镜像,但是用相应的位置替换了大小写标签。此两步过程使保留原始交换机的流量控制变得容易。
有关的更多技术深度switch
,请参考JVM规范,其中描述了switch语句的编译。简而言之,有两种不同的JVM指令可用于切换,具体取决于案例使用的常量的稀疏性。两者都依赖于每种情况下使用整数常量来有效执行。
如果常量密集,则将它们用作指令指针表(指令)的索引(减去最小值后)tableswitch
。
如果常量稀疏,则对lookupswitch
指令的正确大小写进行二进制搜索。
在switch
对String
物体进行除糖时,可能会同时使用这两种指令。在lookupswitch
对散列码的第一开关以找到的情况下的原始位置是合适的。由此产生的序数是自然适合tableswitch
。
两条指令都要求在编译时对分配给每种情况的整数常量进行排序。在运行时,虽然O(1)
性能tableswitch
一般显得比更好O(log(n))
的性能lookupswitch
,它需要一些分析,以确定该表是否是密集足以证明时空权衡。Bill Venners写了一篇很棒的文章,其中更详细地介绍了此内容,同时还深入介绍了其他Java流程控制指令。
在JDK 7之前,enum
可以近似String
基于的开关。这将使用编译器针对每种类型生成的静态valueOf
方法enum
。例如:
Pill p = Pill.valueOf(str);
switch(p) {
case RED: pop(); break;
case BLUE: push(); break;
}
Pill
基于某种方式采取行动,str
我认为if-else是更可取的,因为它允许您处理str
RED,BLUE范围之外的值,而无需捕获异常valueOf
或手动检查与名称的匹配每个枚举类型只会增加不必要的开销。以我的经验,只有在valueOf
以后需要String值的类型安全表示形式时,才可以使用将其转换为枚举。
(hash >> x) & ((1<<y)-1)
对于每个hashCode
不同的字符串,其值集将产生不同的值,并且(1<<y)
少于字符串数的两倍(或等于)至少不超过该值)。
如果您在代码中有一个可以打开String的位置,那么最好将String重构为可能值的枚举,然后可以将其打开。当然,您可以将可能具有的字符串的潜在值限制为枚举中的那些值,这可能会或可能不会。
当然,您的枚举可以包含“ other”的条目和fromString(String)方法,那么您可以
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
}
}
基于整数的开关可以优化为高效代码。基于其他数据类型的开关只能编译为一系列if()语句。
因此,C&C ++仅允许在整数类型上进行切换,因为它与其他类型无关。
C#的设计师认为即使没有优势,样式也很重要。
Java的设计师显然像C的设计师那样思考。
String
从1.7开始的直接用法示例也可能显示:
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;
}
}
James Curran简洁地说:“可以将基于整数的开关优化为非常有效的代码。基于其他数据类型的开关只能编译为一系列if()语句。因此,C&C ++仅允许对整数类型进行开关,因为它与其他类型毫无意义。”
我的观点(仅此而已)是,一旦您开始启用非基本体,就需要开始考虑“等于”与“ ==”。首先,比较两个字符串可能是一个相当漫长的过程,从而增加了上面提到的性能问题。其次,如果需要打开字符串,则需要忽略大小写的字符串,考虑/忽略语言环境的字符串,基于正则表达式的字符串....我赞成这样的决定,该决定可以节省很多时间:语言开发人员会以少量的时间为程序员付出代价。
matched
而not matched
。(不过,没有考虑到[named] groups / etc之类的东西。)
除了上述良好的论据外,我switch
还要补充指出的是,今天很多人都将其视为Java程序过时的一部分(回溯至C时代)。
我并不完全同意这种观点,我认为switch
在某些情况下可以发挥它的作用,至少是因为它的速度,无论如何,它比else if
我在某些代码中看到的一系列级联数字更好。
但是确实,值得一看的是您需要一个开关的情况,看看是否不能用更多的OO代替它。例如Java 1.5+中的枚举,也许是HashTable或其他一些集合(有时我很遗憾,我们没有作为一流公民的(匿名)函数,如Lua(没有开关)或JavaScript)或什至是多态。
如果您没有使用JDK7或更高版本,则可以使用hashCode()
它进行仿真。因为String.hashCode()
通常为不同的字符串返回不同的值,并且对于相等的字符串总是返回相等的值,所以它是相当可靠的(不同的字符串可以产生与注释中提到的@Lii相同的哈希码,例如"FB"
和"Ea"
),请参见documentation。
因此,代码如下所示:
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之间的计算从未改变过)。
case
语句值不必在编译时就可以确定,因此可以很好地工作。
多年来,我们一直在使用(n个开源)预处理器。
//#switch(target)
case "foo": code;
//#end
预处理后的文件名为Foo.jpp,并使用ant脚本处理为Foo.java。
优点是将其处理为可在1.0上运行的Java(尽管通常我们仅支持1.4)。与使用枚举或其他变通方法进行伪造相比,执行此操作(许多字符串切换)要容易得多-代码更易于阅读,维护和理解。IIRC(目前无法提供统计信息或技术推理)也比自然的Java等效更快。
缺点是您不编辑Java,因此工作流程(编辑,处理,编译/测试)要多一些,并且IDE会链接回Java,这有点麻烦(开关变成一系列if / else逻辑步骤)并且不保持开关箱顺序。
我不建议在1.7+上使用它,但是如果您要编写针对较早JVM的Java(因为Joe public很少安装最新版本的Java),它就很有用。
其他答案表明这是在Java 7中添加的,并提供了较早版本的解决方法。这个答案试图回答“为什么”
Java是对C ++过于复杂的反应。它被设计为一种简单的简洁语言。
String在语言中有一些特殊情况处理,但对我来说似乎很清楚,设计师正在尝试将特殊大小写和语法糖的数量保持在最低水平。
由于字符串不是简单的原始类型,因此打开字符串非常复杂。在设计Java时,这并不是一个普遍的功能,并且实际上与极简主义设计并不十分吻合。特别是因为他们决定对字符串不使用特殊情况==,所以对于==不适用的情况下的大小写工作会有些奇怪。
在1.0和1.4之间,语言本身几乎保持不变。Java的大多数增强功能都在库方面。
Java 5改变了一切,该语言得到了实质性扩展。在版本7和8中进行了进一步的扩展。我希望这种态度的改变是由C#的崛起驱动的
JDK-13中的 JEP 354:开关表达式(预览)和JDK-14中的 JEP 361:开关表达式(标准)将扩展 switch语句,因此可以将其用作表达式。
现在你可以:
case L ->
):
“ case L->”开关标签右侧的代码被限制为表达式,块或(为方便起见)throw语句。
为了从switch表达式中产生一个值,
break
with value语句被删除,转而使用一条yield
语句。
public static void main(String[] args) {
switch (args[0]) {
case "Monday", "Tuesday", "Wednesday" -> System.out.println("boring");
case "Thursday" -> System.out.println("getting better");
case "Friday", "Saturday", "Sunday" -> System.out.println("much better");
}
不是很漂亮,但这是Java 6和波纹管的另一种方式:
String runFct =
queryType.equals("eq") ? "method1":
queryType.equals("L_L")? "method2":
queryType.equals("L_R")? "method3":
queryType.equals("L_LR")? "method4":
"method5";
Method m = this.getClass().getMethod(runFct);
m.invoke(this);
Groovy轻而易举;我嵌入了groovy jar并创建了一个groovy
实用程序类来完成所有这些事情,而我发现这些事情在Java中令人生厌(因为我在企业中一直使用Java 6)。
it.'p'.each{
switch (it.@name.text()){
case "choclate":
myholder.myval=(it.text());
break;
}}...
当您使用intellij时,还请注意:
文件->项目结构->项目
文件->项目结构->模块
如果有多个模块,请确保在“模块”选项卡中设置正确的语言级别。
public class StringSwitchCase {
public static void main(String args[]) {
visitIsland("Santorini");
visitIsland("Crete");
visitIsland("Paros");
}
public static void visitIsland(String island) {
switch(island) {
case "Corfu":
System.out.println("User wants to visit Corfu");
break;
case "Crete":
System.out.println("User wants to visit Crete");
break;
case "Santorini":
System.out.println("User wants to visit Santorini");
break;
case "Mykonos":
System.out.println("User wants to visit Mykonos");
break;
default:
System.out.println("Unknown Island");
break;
}
}
}