Scala:为什么扩展trait会破坏'Class.getConstructor()`?
当一个类扩展一个trait时,如果调用Scala:为什么扩展trait会破坏'Class.getConstructor()`?,scala,traits,Scala,Traits,当一个类扩展一个trait时,如果调用getConstructor(),否则会成功,然后抛出一个NoSuchMethodException。为什么会发生这种情况?我们可以做些什么 在下面的代码中,getConstructor()的第一次调用返回时没有错误。第二个提出了例外 trait MyTrait class MyClass(root: String) object Main extends App { val one = new MyClass("foo") one.getCl
getConstructor()
,否则会成功,然后抛出一个NoSuchMethodException
。为什么会发生这种情况?我们可以做些什么
在下面的代码中,getConstructor()
的第一次调用返回时没有错误。第二个提出了例外
trait MyTrait
class MyClass(root: String)
object Main extends App {
val one = new MyClass("foo")
one.getClass.getConstructor(classOf[String])
println("so far so good")
val two = new MyClass("foo") with MyTrait
two.getClass.getConstructor(classOf[String]) // NoSuchMethodException
}
使用零参数构造函数创建一个新的匿名类(该构造函数在内部执行super(“foo”)
),然后创建该新类的实例
classOf[MyClass] eq one.getClass
classOf[MyClass] ne two.getClass
因为在新的匿名类中没有基于字符串的构造函数,所以代码失败
使用零参数构造函数创建一个新的匿名类(该构造函数在内部执行super(“foo”)
),然后创建该新类的实例
classOf[MyClass] eq one.getClass
classOf[MyClass] ne two.getClass
因为在新的匿名类中没有基于字符串的构造函数,所以代码会失败。让我们仔细看看会发生什么
我稍微修改了您的定义,以简化生成的输出。我的Main
对象如下所示:
object App {
def main(args: Array[String]): Unit = {
val one = new MyClass("foo")
one.getClass.getConstructor(classOf[String])
println("so far so good")
val two = new MyClass("foo") with MyTrait
two.getClass.getConstructor(classOf[String]) // NoSuchMethodException
}
}
val foo = "foo"
val two = new MyClass(foo) with MyTrait
编译后,我们得到以下类:App$$anon$1.class
,App$.class
,App.class
,MyClass.class
,MyTrait.class
MyClass.class
和MyTrait.class
都是琐碎的,我不会讨论它们
让我们看看有趣的课程:
App.class
以下是javap-c
输出:
public final class test.App {
public static void main(java.lang.String[]);
Code:
0: getstatic #16 // Field test/App$.MODULE$:Ltest/App$;
3: aload_0
4: invokevirtual #18 // Method test/App$.main:([Ljava/lang/String;)V
7: return
}
如我们所见,它只调用App$.main
方法
App$.class
这是里面的东西:
public final class test.App$ {
public static test.App$ MODULE$;
public static {};
Code:
0: new #2 // class test/App$
3: invokespecial #14 // Method "<init>":()V
6: return
public void main(java.lang.String[]);
Code:
0: new #19 // class test/MyClass
3: dup
4: ldc #21 // String foo
6: invokespecial #24 // Method test/MyClass."<init>":(Ljava/lang/String;)V
9: astore_2
10: aload_2
11: invokevirtual #28 // Method test/MyClass.getClass:()Ljava/lang/Class;
14: iconst_1
15: anewarray #30 // class java/lang/Class
18: dup
19: iconst_0
20: ldc #32 // class java/lang/String
22: aastore
23: invokevirtual #36 // Method java/lang/Class.getConstructor:([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
26: pop
27: getstatic #41 // Field scala/Predef$.MODULE$:Lscala/Predef$;
30: ldc #43 // String so far so good
32: invokevirtual #47 // Method scala/Predef$.println:(Ljava/lang/Object;)V
35: new #7 // class test/App$$anon$1
38: dup
39: invokespecial #48 // Method test/App$$anon$1."<init>":()V
42: astore_3
43: aload_3
44: invokevirtual #28 // Method test/MyClass.getClass:()Ljava/lang/Class;
47: iconst_1
48: anewarray #30 // class java/lang/Class
51: dup
52: iconst_0
53: ldc #32 // class java/lang/String
55: aastore
56: invokevirtual #36 // Method java/lang/Class.getConstructor:([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
59: pop
60: return
private test.App$();
Code:
0: aload_0
1: invokespecial #54 // Method java/lang/Object."<init>":()V
4: aload_0
5: putstatic #56 // Field MODULE$:Ltest/App$;
8: return
}
public final class test.App$$anon$1 extends test.MyClass implements test.MyTrait {
public test.App$$anon$1();
Code:
0: aload_0
1: ldc #16 // String foo
3: invokespecial #19 // Method test/MyClass."<init>":(Ljava/lang/String;)V
6: return
}
在这里,我们终于看到了我们的“foo”
(在第1行)<代码>ldc
表示“负载常数”;“#16”是对表中该常数的引用;注释说它的类型是String
,值是foo
第3行是对MyClass.“:(Ljava/lang/String;)V
的调用
TLDR优化器在自动生成的类的构造函数中隐藏常量。所以构造函数本身没有任何参数
PS:如果我们像这样更改代码:
object App {
def main(args: Array[String]): Unit = {
val one = new MyClass("foo")
one.getClass.getConstructor(classOf[String])
println("so far so good")
val two = new MyClass("foo") with MyTrait
two.getClass.getConstructor(classOf[String]) // NoSuchMethodException
}
}
val foo = "foo"
val two = new MyClass(foo) with MyTrait
然后我们得到一个自动生成的类,该类具有接受字符串参数的构造函数:
public final class test.App$$anon$1 extends test.MyClass implements test.MyTrait {
public test.App$$anon$1(java.lang.String);
Code:
0: aload_0
1: aload_1
2: invokespecial #17 // Method test/MyClass."<init>":(Ljava/lang/String;)V
5: return
}
public final class test.App$$anon$1扩展test.MyClass实现test.MyTrait{
public test.App$$anon$1(java.lang.String);
代码:
0:aload_0
1:aload_1
2:invokespecial#17//methodtest/MyClass.“:(Ljava/lang/String;)V
5:返回
}
让我们仔细看看发生了什么
我稍微修改了您的定义,以简化生成的输出。我的Main
对象如下所示:
object App {
def main(args: Array[String]): Unit = {
val one = new MyClass("foo")
one.getClass.getConstructor(classOf[String])
println("so far so good")
val two = new MyClass("foo") with MyTrait
two.getClass.getConstructor(classOf[String]) // NoSuchMethodException
}
}
val foo = "foo"
val two = new MyClass(foo) with MyTrait
编译后,我们得到以下类:App$$anon$1.class
,App$.class
,App.class
,MyClass.class
,MyTrait.class
MyClass.class
和MyTrait.class
都是琐碎的,我不会讨论它们
让我们看看有趣的课程:
App.class
以下是javap-c
输出:
public final class test.App {
public static void main(java.lang.String[]);
Code:
0: getstatic #16 // Field test/App$.MODULE$:Ltest/App$;
3: aload_0
4: invokevirtual #18 // Method test/App$.main:([Ljava/lang/String;)V
7: return
}
如我们所见,它只调用App$.main
方法
App$.class
这是里面的东西:
public final class test.App$ {
public static test.App$ MODULE$;
public static {};
Code:
0: new #2 // class test/App$
3: invokespecial #14 // Method "<init>":()V
6: return
public void main(java.lang.String[]);
Code:
0: new #19 // class test/MyClass
3: dup
4: ldc #21 // String foo
6: invokespecial #24 // Method test/MyClass."<init>":(Ljava/lang/String;)V
9: astore_2
10: aload_2
11: invokevirtual #28 // Method test/MyClass.getClass:()Ljava/lang/Class;
14: iconst_1
15: anewarray #30 // class java/lang/Class
18: dup
19: iconst_0
20: ldc #32 // class java/lang/String
22: aastore
23: invokevirtual #36 // Method java/lang/Class.getConstructor:([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
26: pop
27: getstatic #41 // Field scala/Predef$.MODULE$:Lscala/Predef$;
30: ldc #43 // String so far so good
32: invokevirtual #47 // Method scala/Predef$.println:(Ljava/lang/Object;)V
35: new #7 // class test/App$$anon$1
38: dup
39: invokespecial #48 // Method test/App$$anon$1."<init>":()V
42: astore_3
43: aload_3
44: invokevirtual #28 // Method test/MyClass.getClass:()Ljava/lang/Class;
47: iconst_1
48: anewarray #30 // class java/lang/Class
51: dup
52: iconst_0
53: ldc #32 // class java/lang/String
55: aastore
56: invokevirtual #36 // Method java/lang/Class.getConstructor:([Ljava/lang/Class;)Ljava/lang/reflect/Constructor;
59: pop
60: return
private test.App$();
Code:
0: aload_0
1: invokespecial #54 // Method java/lang/Object."<init>":()V
4: aload_0
5: putstatic #56 // Field MODULE$:Ltest/App$;
8: return
}
public final class test.App$$anon$1 extends test.MyClass implements test.MyTrait {
public test.App$$anon$1();
Code:
0: aload_0
1: ldc #16 // String foo
3: invokespecial #19 // Method test/MyClass."<init>":(Ljava/lang/String;)V
6: return
}
在这里,我们终于看到了我们的“foo”
(在第1行)<代码>ldc
表示“负载常数”;“#16”是对表中该常数的引用;注释说它的类型是String
,值是foo
第3行是对MyClass.“:(Ljava/lang/String;)V
的调用
TLDR优化器在自动生成的类的构造函数中隐藏常量。所以构造函数本身没有任何参数
PS:如果我们像这样更改代码:
object App {
def main(args: Array[String]): Unit = {
val one = new MyClass("foo")
one.getClass.getConstructor(classOf[String])
println("so far so good")
val two = new MyClass("foo") with MyTrait
two.getClass.getConstructor(classOf[String]) // NoSuchMethodException
}
}
val foo = "foo"
val two = new MyClass(foo) with MyTrait
然后我们得到一个自动生成的类,该类具有接受字符串参数的构造函数:
public final class test.App$$anon$1 extends test.MyClass implements test.MyTrait {
public test.App$$anon$1(java.lang.String);
Code:
0: aload_0
1: aload_1
2: invokespecial #17 // Method test/MyClass."<init>":(Ljava/lang/String;)V
5: return
}
public final class test.App$$anon$1扩展test.MyClass实现test.MyTrait{
public test.App$$anon$1(java.lang.String);
代码:
0:aload_0
1:aload_1
2:invokespecial#17//methodtest/MyClass.“:(Ljava/lang/String;)V
5:返回
}
有趣的是,如果您使用变量而不是常量,那么scala将生成带有字符串
参数的构造函数。@Oleg Pyzhcov:毫无疑问,这个答案是正确的,但这是否有文档记录?你是通过阅读源代码了解到这一点的吗?@talex:你确定吗?我将val-two
更改为var-two
,但仍然引发异常。@AdamMackler我的意思是,如果您将“foo”
替换为foo
并声明var-foo=“foo”
@talex,您确定这是真的吗?我在two
的定义之上定义了var foo=“foo”
,然后将val two=new MyClass(“foo”)
更改为val two=new MyClass(foo)
但仍然引发了异常。有趣的是,如果您使用变量而不是常量,scala将生成带有字符串
参数的构造函数。@Oleg Pyzhcov:毫无疑问,这个答案是正确的,但这是否有文档记录?你是通过阅读源代码了解到这一点的吗?@talex:你确定吗?我将val-two
更改为var-two
,但仍然引发异常。@AdamMackler我的意思是,如果您将“foo”
替换为foo
并声明var-foo=“foo”
@talex,您确定这是真的吗?我在two
的定义之上定义了var foo=“foo”
,然后将val two=new MyClass(“foo”)
更改为val two=new MyClass(foo)
,并且仍然引发了异常。我想知道如果您生成final val foo=“foo”
,它是否会返回到内联?您的final val foo
是什么意思val
已经意味着您不能更改它,而且final val foo
甚至连co都不能更改