【发布时间】:2016-12-30 21:19:07
【问题描述】:
考虑到接口(它们非常大并且由语言定义生成):
interface VisitorA {
default void visit(ASTA1 node) {...}
...
default void visit(ASTA2000 node) {...}
}
interface VisitorB extends VisitorA {
default void visit(ASTB1 node) {...}
...
default void visit(ASTB1000 node) {...}
// due to language embedding all visit methods of VisitorA
// must be overwritten
@Override
default void visit(ASTA1 node) {...}
...
@Override
default void visit(ASTA2000 node) {...}
}
interface VisitorC extends VisitorA {
default void visit(ASTC1 node) {...}
...
default void visit(ASTC1000 node) {...}
// due to language embedding all visit methods of VisitorA
// must be overwritten
@Override
default void visit(ASTA1 node) {...}
...
@Override
default void visit(ASTA2000 node) {...}
}
interface VisitorD extends VisitorB, VisitorC {
default void visit(ASTD1 node) {...}
...
default void visit(ASTD1000 node) {...}
// due to language embedding all visit methods of VisitorA,
// VisitorB, and VisitorC must be overwritten
@Override
default void visit(ASTA1 node) {...}
...
@Override
default void visit(ASTA2000 node) {...}
@Override
default void visit(ASTB1 node) {...}
...
@Override
default void visit(ASTB1000 node) {...}
@Override
default void visit(ASTC1 node) {...}
...
@Override
default void visit(ASTC1000 node) {...}
}
现在编译 VisitorA 接口(包含大约 2.000 个重载方法)大约需要 10 秒。 编译VisitorB 和VisitorC 接口分别需要大约1.5 分钟。 但是当我们尝试编译VisitorD接口时,Java 8编译器大约需要7分钟!
- 有人知道为什么编译VisitorD 需要这么多时间吗?
- 是不是因为继承了默认方法?
- 还是因为钻石星座,VisitorB和VisitorC都扩展了VisitorA,VisitorD又扩展了VisitorB和VisitorC?
我们已经尝试过,以下解决方案有所帮助:
interface VisitorAPlain {
void visit(ASTA1 node);
...
void visit(ASTA2000 node);
}
interface VisitorA extends VisitorAPlain {
... // has same default methods as VisitorA above
}
interface VisitorBPlain extends VisitorAPlain {
void visit(ASTB1 node);
...
void visit(ASTB1000 node);
}
interface VisitorB extends VisitorBPlain {
... // has same default methods as VisitorB above
}
interface VisitorCPlain extends VisitorAPlain {
void visit(ASTC1 node);
...
void visit(ASTC1000 node);
}
interface VisitorC extends VisitorCPlain {
... // has same default methods as VisitorC above
}
interface VisitorD extends VisitorBPlain, VisitorCPlain {
default void visit(ASTD1 node) {...}
...
default void visit(ASTD1000 node) {...}
// due to language embedding all visit methods of VisitorAPlain,
// VisitorBPlain, and VisitorCPlain must be overwritten
@Override
default void visit(ASTA1 node) {...}
...
default void visit(ASTA2000 node) {...}
@Override
default void visit(ASTB1 node) {...}
...
default void visit(ASTB1000 node) {...}
@Override
default void visit(ASTC1 node) {...}
...
default void visit(ASTC1000 node) {...}
}
现在visitorD的编译时间只需要2分钟左右。 但这仍然很多。
- 有人知道如何将 VisitorD 的编译时间减少到几秒钟吗?
- 如果我们去掉VisitorD的两个扩展关系
extends VisitorBPlain, VisitorCPlain,那么这个接口的编译时间大约需要15s——尽管它有大约5000个默认方法。但是出于强制转换的原因,我们需要 VisitorD 与 VisitorB 和 VisitorC 兼容(通过直接扩展或具有中间普通接口的间接扩展)。
我还阅读了类似问题的答案: slow JDK8 compilation 但问题似乎出在泛型类型推断上: “在基于泛型目标类型的重载解析方面,Java 8 中存在严重的性能退化。”
所以这有点不同,如果有人有小费或好的 解释为什么会这样;非常感谢。
谢谢你, 迈克尔
【问题讨论】:
-
抱歉 - 没有答案,但我很好奇 - 这些文件有多大?我有一个包含大约 1000 个文件的项目,总共大约 150,000 行,而使用 Maven,编译需要 15 秒多一点。您必须有一些主要文件。
-
这个文件很大。它有大约 270kB 大。我上传了,大家自己看吧:drive.google.com/open?id=0B6L6K365bELNbXFhZVp6MG55RU0
-
这是由所有具有相同名称的方法引起的。因此,要检查覆盖是否正确(不会意外覆盖桥),必须相互检查每种方法,这会变得二次(或更糟)。在一个类中拥有数千个具有相同名称的方法会给重载选择/检查带来很大压力,这就是您看到这种情况的原因。 (请注意,这在手写代码中并不常见,只是生成的代码。)
-
将方法命名为
visitAst1(AST1)。 (无论如何,大多数访问者都会这样做。) -
AST1中的accept(Visitor v)方法委托给v.visitAst1(this)。
标签: java interface java-8 compile-time default-method