Dan*_*ina 8 java generics compilation
我们在代码中简化了泛型的一些定义和用法.现在我们得到一个有趣的案例,举个例子:
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() {};
}
}
Run Code Online (Sandbox Code Playgroud)
现在看看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 Javac 7上正确编译,但在Javac 6上没有编译.这是一个Javac 6错误.所以这意味着我们的代码没有任何问题,我们可以坚持下去.问题已经回答了,在我自己的回答超过两天之后我会将其标记为.感谢大家的建设性反馈.
Sam*_*Sam 10
在java中,如果在方法签名的Parameter或Return Type中使用泛型类型,则可以执行泛型方法.在您的示例泛型doSomethingWeird方法中,但从未在方法签名中使用它.见以下样本:
class MyWeirdClass
{
public void entryPoint()
{
doSomethingWeird(new MyClass_1());
}
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 a)
{
T obj = getMyClass();
obj.methodFromA();
obj.methodFromB();
}
}
Run Code Online (Sandbox Code Playgroud)
这段代码很好用.
JLS(Java语言规范)在通用方法部分中说:
Type parameters of generic methods need not be provided explicitly when a
generic method is invoked. Instead, they are almost always inferred as specified in
§15.12.2.7
Run Code Online (Sandbox Code Playgroud)
通过这个报价,当你不使用T的doSomethingWeird方法签名,您指定原始类型什么T的调用时间(entryPoint方法)?
这是 OpenJDK 人员对我的问题的回答:
这些失败是由于 JDK 6 编译器未正确实现类型推断而导致的。为了消除所有这些问题,JDK 7 编译器投入了大量精力(您的程序在 JDK 7 中编译得很好)。然而,其中一些推理改进需要源不兼容的更改,这就是为什么我们无法在 JDK 6 版本中反向移植这些修复。
所以这对我们来说意味着:我们的代码绝对没有任何问题,并且也得到了 Oracle 的官方支持。我们也可以坚持这种类型的代码,并使用 Javac 7 和 target=1.6 进行我们的 Maven 构建,而在 Eclipse 中进行开发将保证我们不使用 Java 7 API :D yaaahyyy!!!