为什么可以';我们不能在Java7+;中打开类吗;?
在我看来,这样的switch语句很有意义,但它给出了一个编译错误:为什么可以';我们不能在Java7+;中打开类吗;?,java,class,switch-statement,java-8,java-7,Java,Class,Switch Statement,Java 8,Java 7,在我看来,这样的switch语句很有意义,但它给出了一个编译错误: public void m(Class c) { switch (c) { case SubClassOfC1.class : //do stuff; break; case SubClassOfC2.class : //do stuff; break; } } 但是,不支持打开类。原因是什么 我并不是在尝试解决实例的问题,实际上是在类级别,我有一些操作要执行,没有实例 编译错误发生在
public void m(Class c) {
switch (c) {
case SubClassOfC1.class : //do stuff; break;
case SubClassOfC2.class : //do stuff; break;
}
}
但是,不支持打开类。原因是什么
我并不是在尝试解决实例的问题,实际上是在类级别,我有一些操作要执行,没有实例
编译错误发生在Subassofc1和Subassofc2附近:需要常量表达式。
基于:
开关使用byte、short、char和int基元数据类型。它还适用于枚举类型(在枚举类型中讨论)、字符串类和一些包装某些基本类型的特殊类:Character、Byte、Short和Integer
因此,本质上,它只适用于这些类型,而不适用于其他类型<代码>类
不是其中之一
限制的原因是,正如您自己在一篇评论中所说,开关表是由int
索引的。上述所有类型都可以轻松地转换为int
(包括String
,通过散列),而类
则不是。根据开关
只支持有限的一组数据类型:
开关使用byte、short、char和int基元数据类型。它还适用于枚举类型(在枚举类型中讨论)、字符串类和一些包装某些基本类型的特殊类:Character、Byte、Short和Integer
我对为什么会出现这种情况的猜测是:因为编译器很难切换到相对简单的数据类型以外的其他类型。这是因为我们只能切换到或 : 表达式的类型必须是char、byte、short、int、Character、byte、short、Integer、String或枚举类型(),否则会发生编译时错误 要想知道为什么会这样,请考虑一下Java编译器试图编译switch语句时发生的优化
- 它想绝对、积极地保证平等
- 它希望能够最大限度地提高所有情况下的性能(分支预测)
- 它希望有一种有效、一致的方法将常量表达式转换为整数查找表(这就是为什么不支持
和long
和float
,而支持double
的原因)String
switch
语句中支持的String
。这是因为编译器使用开关字符串
到开关int
的幕后转换,如下所示。快速总结:
此代码:
public class StringInSwitchCase {
public static void main(String[] args) {
String mode = args[0];
switch (mode) {
case "ACTIVE":
System.out.println("Application is running on Active mode");
break;
case "PASSIVE":
System.out.println("Application is running on Passive mode");
break;
case "SAFE":
System.out.println("Application is running on Safe mode");
}
}
}
成为以下代码:
public class StringInSwitchCase {
public StringInSwitchCase() {}
public static void main(string args[]) {
String mode = args[0];
String s;
switch ((s = mode).hashCode()) {
default:
break;
case -74056953:
if (s.equals("PASSIVE")) {
System.out.println("Application is running on Passive mode");
}
break;
case 2537357:
if (s.equals("SAFE")) {
System.out.println("Application is running on Safe mode");
}
break;
case 1925346054:
if (s.equals("ACTIVE")) {
System.out.println("Application is running on Active mode");
}
break;
}
}
}
我们不能以同样的方式可靠地将Class
对象转换为int。类不重写hashCode,它使用
还要注意的是,同一个类并不总是相同的
类
,如果它加载了不同的类加载器
,,有趣的是,到目前为止,所有的答案基本上都是“因为规范这么说”,这是正确的,但并不真正令人满意。在Java7之前,String
s是不允许的,这通常被视为是刻在石头上的
但是技术障碍不应该驱动语言设计。如果没有办法将它编译成高效的代码,它可能仍然会被编译成相当于If…else…
子句的形式,并且在源代码简洁性方面仍有优势。对于String
值,有一种有效的方法。只需切换不变hashcode并对匹配候选项执行equals
检查。事实上,规范并不要求使用哈希代码,它可以是任何不变的int
属性,例如长度或第一个字符,但每个字符串的值应该不同
类似地,可以在类
对象上切换。它的哈希代码不能保证相同,但每个类都有一个带有常量哈希代码的常量名称。例如,以下工作:
public class ClassSwitch {
static final class Foo {}
static final class Bar {}
static final class Baz {}
public static void main(String... arg) {
Class<?> cl=Bar.class;
switch(cl.getSimpleName().hashCode()) {
case 70822:
if(cl==Foo.class) {
System.out.println("case Foo:");
}
break;
case 66547:
if(cl==Bar.class) {
System.out.println("case Baz:");
}
break;
case 66555:
if(cl==Baz.class) {
System.out.println("case Baz:");
}
break;
}
}
}
公共类类开关{
静态最终类Foo{}
静态最终类条{}
静态最终类Baz{}
公共静态void main(字符串…参数){
类别cl=棒材类别;
开关(cl.getSimpleName().hashCode()){
判例70822:
if(cl==Foo.class){
System.out.println(“casefoo:”);
}
打破
判例66547:
如果(cl==Bar.class){
System.out.println(“案例Baz:”);
}
打破
判例66555:
if(cl==Baz.class){
System.out.println(“案例Baz:”);
}
打破
}
}
}
我使用了简单名称而不是限定名称,因此此示例代码与包无关。但我认为,情况很清楚。可以为任何类型的对象实现有效的switch
语句,这些对象具有可在编译时预测的常量int
属性。也就是说,没有理由不支持long
开关。有很多方法可以从long
计算出合适的int
所以还有另一个重要的决定要做
这个功能真的有好处吗?它看起来像是代码气味,甚至添加了String
支持也是有争议的。它不会添加新的可能性,因为对于少量的类,您可以使用if
-else
,对于较大的类,也可以使用HashMap
。而且它看起来不像是经常需要的东西,因此值得扩展语言规范,即使它只是需要添加的一句话
这些是女秘书
import java.util.function.Consumer;
public class SwitchClass<T> {
private static final SwitchClass<?> EMPTY = new SwitchClass<Object>(null) {
@Override
public <S> SwitchClass<Object> when(Class<S> subClass,
Consumer<? super S> consumer) { return this; }
@Override
public void orElse(Consumer<? super Object> consumer) { }
};
final T obj;
private SwitchClass(T obj) {
this.obj = obj;
}
@SuppressWarnings("unchecked")
public <S> SwitchClass<T> when(Class<S> subClass,
Consumer<? super S> consumer) {
if (subClass.isInstance(obj)) {
consumer.accept((S) obj);
return (SwitchClass<T>) EMPTY;
}
return this;
}
public void orElse(Consumer<? super T> consumer) {
consumer.accept(obj);
}
public static <T> SwitchClass<T> of(T t) {
return new SwitchClass<>(t);
}
}
SwitchClass.of(obj)
.when(Integer.class, i -> System.out.println("Integer: "+i.intValue()))
.when(Double.class, d -> System.out.println("Double: "+d.doubleValue()))
.when(Number.class, n -> System.out.println("Some number: "+n))
.when(String.class, str -> System.out.println("String of length "+str.length()))
.orElse(o -> System.out.println("Unknown object: "+o));