【发布时间】:2012-02-21 07:58:22
【问题描述】:
我们一直在简化代码中泛型的一些定义和使用。 现在我们得到一个有趣的案例,举个例子:
public class MyWeirdClass {
public void entryPoint() {
doSomethingWeird();
}
@SuppressWarnings( "unchecked" )
private <T extends A & B> T getMyClass() {
if ( System.currentTimeMillis() % 2 == 0 ) {
return (T) new MyClass_1();
} else {
return (T) new MyClass_2();
}
}
private <T extends A & B> void doSomethingWeird() {
T obj = getMyClass();
obj.methodFromA();
obj.methodFromB();
}
static interface A {
void methodFromA();
}
static interface B {
void methodFromB();
}
static class MyClass_1 implements A, B {
public void methodFromA() {};
public void methodFromB() {};
}
static class MyClass_2 implements A, B {
public void methodFromA() {};
public void methodFromB() {};
}
}
现在看一下 MyWeirdClass 中的方法 'doSeomthingWeird(): 此代码将使用 eclipse JDT 编译器正确编译,但是在使用 Oracle 编译器时会失败。由于 JDT 能够生成工作字节码,这意味着在 JVM 级别,这是有效的代码,并且“只有”Oracle 编译器不允许编译这些脏(!?)的东西。 我们知道 Oracle 的编译器不会接受调用 'T obj = getMyClass();'因为 T 不是真正存在的类型。但是,既然我们知道返回的对象实现了 A 和 B,为什么不允许呢? (JDT 编译器和 JVM 会这样做)。 另请注意,由于泛型代码仅在私有方法内部使用,我们不希望在类级别公开它们,用泛型定义污染外部代码,我们不感兴趣(从类外部)。
教科书的解决方案是创建一个接口 AB 扩展 A,B 但是由于我们有大量的接口用于不同的组合并且来自不同的模块,因此为所有组合创建共享接口将显着增加“虚拟”接口的数量,最终使代码的可读性降低。理论上,为了覆盖所有情况,需要对不同的包装器接口进行多达 N 次排列。 “面向业务的工程师”(其他人称其为“懒惰的工程师”)解决方案是这样保留代码并开始仅使用 JDT 来编译代码。 编辑:这是 Oracle 的 Javac 6 中的一个错误,并且在 Oracle 的 Javac 7 上也可以正常工作
什么意思?采用这种“策略”有什么隐患吗?
为了避免讨论(对我来说)不相关的点: 我不是在问为什么上面的代码不能在 Oracle 的编译器上编译我知道原因,如果在使用其他编译器时可以完美运行,我不想在没有充分理由的情况下修改这种代码。 请专注于方法'doSomethingWeird()'的定义和用法(不给出具体类型)。 有没有一个很好的理由,为什么我们不应该只使用允许编写和编译此代码的 JDT 编译器并停止使用 Oracle 的编译器进行编译,Oracle 的编译器将不接受上面的代码? (感谢输入)
编辑:上面的代码在 Oracle Javac 7 上编译正确,但在 Javac 6 上编译不正确。这是一个 Javac 6 错误。所以这意味着我们的代码没有任何问题,我们可以坚持下去。 问题已回答,我会在两天超时后将其标记为我自己的答案。 感谢大家的建设性反馈。
【问题讨论】:
-
oracle 编译器是标准的,默认与外部工具(Maven、Ant 等)一起使用。而且看起来 Oracle 是对的,而 Eclipse 编译器是错误的并且有一个漏洞。因此,这个错误很有可能在未来的版本中得到修复,这将使您的代码完全无法编译。你不想这样,是吗?
-
您的示例根本没有显示使用泛型的相关性。你引入一个虚拟泛型,选择你的词,避免一个虚拟接口?对不起 - 这没有任何意义。看你的代码,没有看你的评论,我的想法就是创建一个接口 AB,它简化了整个事情。你的 doSomethingWeird 方法有一个从不使用的类型参数,并且总是返回相同的东西,忽略这个无用的参数。
-
您好,感谢您的反馈。这是我的担心,我不想走冒险的路,这可能会导致将来出现一些大问题。问题是,不清楚这是否是 JDT 编译器中的“错误”,据我了解,这是 JLS 未涵盖的情况,所以如果我有一些保证,至少JDT,将继续支持这个...功能。然后我们可能会在我们的代码中保留一些带有这些泛型(错误)使用的案例。我最好在 JDT 和 OpenJDK 邮件列表上问这个问题...
-
@DaniloTommasina 有一个完整的术语来表示“[此处的语言规范名称] 未涵盖”,它是未定义的行为。它在 Java 中并不常见,因为它的回旋余地比 C++ 小,但在某些情况下,这就是其中之一。要回答您的问题 -- 否,您不能确定 JDT 是否会继续支持这一点。它现在可能会生成“工作”(无论如何根据您的定义)代码,明天可以免费生成独角兽。
-
@TC1 是的,我同意这一点。我在 JDT 和 OpenJDK 编译器邮件列表上都询问过……让我们看看他们对此有何看法;)
标签: java generics compilation