Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/333.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
为什么可以';我们不能在Java7+;中打开类吗;?_Java_Class_Switch Statement_Java 8_Java 7 - Fatal编程技术网

为什么可以';我们不能在Java7+;中打开类吗;?

为什么可以';我们不能在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; } } 但是,不支持打开类。原因是什么 我并不是在尝试解决实例的问题,实际上是在类级别,我有一些操作要执行,没有实例 编译错误发生在

在我看来,这样的switch语句很有意义,但它给出了一个编译错误:

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));