新的Java17型模式匹配开关如何在引擎盖下工作?由于该功能相当新,本问题不讨论它。
提醒:要使此代码在Java 17下工作,需要启用预览功能
public static void test (Object o) {
System.out.println(switch(o){
case Number n -> "number " + n;
case Enum e -> "enum " + e;
case String s -> "string " + s;
default -> "other " + o;
});
}
使用javap-c对上述代码进行了反汇编:
0: getstatic #7 // Field java/lang/System.out:Ljava/io/PrintStream;
3: aload_0
4: dup
5: invokestatic #13 // Method java/util/Objects.requireNonNull:(Ljava/lang/Object;)Ljava/lang/Object;
8: pop
9: astore_1
10: iconst_0
11: istore_2
12: aload_1
13: iload_2
14: invokedynamic #19, 0 // InvokeDynamic #0:typeSwitch:(Ljava/lang/Object;I)I
19: tableswitch { // 0 to 2
0: 44
1: 58
2: 74
default: 90
}
44: aload_1
45: checkcast #23 // class java/lang/Number
48: astore_3
49: aload_3
50: invokedynamic #25, 0 // InvokeDynamic #1:makeConcatWithConstants:(Ljava/lang/Number;)Ljava/lang/String;
55: goto 96
58: aload_1
59: checkcast #29 // class java/lang/Enum
62: astore 4
64: aload 4
66: invokedynamic #31, 0 // InvokeDynamic #2:makeConcatWithConstants:(Ljava/lang/Enum;)Ljava/lang/String;
71: goto 96
74: aload_1
75: checkcast #34 // class java/lang/String
78: astore 5
80: aload 5
82: invokedynamic #36, 0 // InvokeDynamic #3:makeConcatWithConstants:(Ljava/lang/String;)Ljava/lang/String;
87: goto 96
90: aload_0
91: invokedynamic #39, 0 // InvokeDynamic #4:makeConcatWithConstants:(Ljava/lang/Object;)Ljava/lang/String;
96: invokevirtual #42 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
99: return
鉴于这一关键点:
14: invokedynamic #19, 0 // InvokeDynamic #0:typeSwitch:(Ljava/lang/Object;I)I
看起来Java在自动生成的int-typeSwitch(object,int)方法中将对象转换为int,我看不到生成的代码。这种转换的目标是能够在int值上使用常规的切换表。
起初,我认为使用了的hashCode,即对象。getClass()。hashCode(),就像切换字符串一样,但实际上类对象的哈希代码在JVM的连续运行中并不是常数。因此,除非在每次执行时都重建映射表,否则这是不可能的,我怀疑的是。
因此,问题是:
这个typeSwitch方法做什么?
它是否比一系列if instanceof或if instanceof更聪明?这可能吗?
传递额外int的目的是什么?
这个typeSwitch()
方法做什么?
invokeDynamic指令(第一次命中时)调用SwitchBootstraps。typeSwitch()方法。然后,此方法返回应该执行的方法调用(这是InvokedDynamic通常所做的)。
SwitchBootstraps的最后一个参数。typeSwitch()方法(标签参数)在本例中是开关中的类列表:编号。类,枚举。类,字符串。类别
指令的最终执行)。SwitchBootstraps.typeSwitch()
bootstrap方法检查标签
参数的正确性,然后为执行有效处理的SwitchBootstraps.doTypeSwitch()
方法返回ConstantCallSite
(例如,调用动态
如果您查看SwitchBootstraps.doTypeSwitch()
的作用:它遍历类列表并返回第一个找到的匹配项。
额外传递的int的目的是什么?
由于这种情况,需要附加参数(startIndex
):
public static void test(Object o) {
System.out.println(switch(o){
case Number n && n.intValue() >= 10 -> "large number " + n;
case Number n -> "small number "+ n;
case Enum e -> "enum " + e;
case String s -> "string " + s;
default -> "other " + o;
});
}
现在有两种情况与类java.lang.Number
匹配,第一种情况由条件n保护
如果我调用test(5)
,则将使用要测试的对象(Integer.valueOf(5)
)调用doTypeSwitch()方法,即0的startIndex和类列表。它迭代类列表,并在找到第一个编号条目并返回其索引(0)时立即停止。
它开始处理第一个案例并检查防护装置。因为我用值5调用它,所以保护失败,代码循环回doTypeSwitch()调用。然而,这次它是用对象来调用的,以测试(Integer.valueOf(5)
),一个1的开始索引和相同的类列表。
代码是否比列表更智能(或更快)if(o instanceof Number n
在当前状态下肯定不会更快。但是请记住,这是第一次迭代(https://openjdk.java.net/jeps/406)中的预览功能:Java架构师正在试验此功能,获得
typeSwitch
的最佳性能可能不是第一个问题。
这个特性已经有了第二次迭代(https://openjdk.java.net/jeps/420),一旦设计完成,未来的版本可能会提高性能。
但即便如此:要使用if/else if链实现测试(对象o)
方法,您必须将该方法分为两部分:
public static void test2(Object o) {
System.out.println(check2(o));
}
private static String check2(Object o) {
if (o instanceof Number n && n.intValue() >= 10)
return "large number " + n;
else if (o instanceof Number n)
return "small number "+ n;
else if (o instanceof Enum e)
return "enum " + e;
else if (o instanceof String s)
return "string " + s;
else
return "other " + o;
}
这大约是行数的两倍。
那(让它更聪明)可能吗?
这至少是可以想象的。可以重新排列类标签,可以根据类标签的类层次结构将它们做成树结构,或者根据最常用的路径在运行时执行一些其他智能操作。
Java的switch语句是如何工作的?它如何将所使用变量的值与案例部分中给出的值进行比较?它是否使用或,还是完全是其他原因? 我主要对1.7之前的版本感兴趣。
但我对西农在台面下是如何工作仍有一些疑问。我想我是在说Sinon,但这个问题可能适用于所有其他设计为mock/stub/spy的库。 在过去的几年里,我工作最多的语言是Java。在Java中,我使用Mockito来模拟/存根依赖项和依赖项注入。我曾经导入这个类,用注释这个字段,并将这个mock作为param传递给被测试的类。对我来说,很容易看出我在做什么:模仿一个类,并将模仿作为param传递。
@Component表示给定的类将是给定上下文中的单例,@aspect表示在运行时/编译期间,一个方面类的内容将以某种方式编织到目标类中--例如,这个目标类不是单例而是原型。我最后的结局是什么?
除了阅读github中的代码之外,是否有关于signalr.redis包如何工作的白皮书类型的文档?具体地说,我想知道它为Redis添加了哪些键、更新/删除策略等。当查看Redis内部时,我只看到以下调用中指定的一个键(即“signalr.Redis.sample”): 这把钥匙好像是Redis的柜台。我假设正在创建其他键并迅速删除,以方便连接到Redis的每个应用服务器之间的消息。
问题内容: 在阅读了戴夫·切尼(Dave Cheney)关于Go的地图的博客文章之后,对我来说,还有几件事尚不清楚。 TLDR: 为什么它们无序? 实际值存储在哪里? 深入研究运行时程序包后,我发现基本的映射结构如下: -是存储区数组,其中索引是键的哈希值的低位,其中存储区为: ..好吧,这只是每个项目是键的哈希值的第一个字节的数组。键值对存储为(每个存储桶八对)。但是到底在哪里?考虑到映射可能包
问题内容: 想知道是否有一种方法可以在Swift中执行以下操作。 我知道我还有其他方法可以使这个人为的示例工作,例如是否使用开关,而是使用开关,但是我正在寻找一种解决方案,具体涉及将开关模式匹配到阵列。谢谢! 问题答案: 您可以定义一个自定义模式匹配运算符,该运算符 将一个数组作为“模式”和一个值: 类似的运算符已经存在,例如间隔: