请考虑以下代码:
public class Outer<T> {
public class Inner{
}
public static <T> Outer<T>.Inner get(){
Object o = new Object();
return (Outer<T>.Inner)o;
}
public static void main(String[] args) throws Exception {
Outer.<String>get();
}
}
此代码在Eclipse中成功编译,但无法在javac
中编译:
Outer.java:10: ')' expected
return (Outer<T>.Inner)o;
^
Outer.java:10: ';' expected
return (Outer<T>.Inner)o;
^
Outer.java:10: illegal start of expression
return (Outer<T>.Inner)o;
^
3 errors
这是javac
还是Eclipse中的错误?
如果我将演员表更改为(Outer.Inner)o
,它会编译,但会有警告:
蚀:
Outer.Inner is a raw type. References to generic type Outer<T>.Inner should be parameterized
javac的:
Outer.java:10: warning: [unchecked] unchecked conversion
found : Outer.Inner
required: Outer<T>.Inner
return (Outer.Inner)o;
^
1 warning
Javac版本: 1.6.0_21
答案 0 :(得分:4)
我发现这是javac编译器中已经修复的错误。 JDK 7b100编译这个罚款。
请参阅http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6665356
答案 1 :(得分:3)
最有趣的是,除非我想念Java泛型,否则
return (Outer<T>.Inner) o;
和
return (Outer.Inner) o;
两者都编译为相同的字节码。
第一行的问题发生在解析 - 意味着javac和Eclipse不使用相同的Java源代码解析器。您应该问一个关于Eclipse JDT的java解析器和javac之间存在什么差异的问题。 (或者在Eclipse上发布一个错误)。
如果你坚持保持这种行为(我建议将Inner
重构为静态内部类),你可以使用带有字段分配的@SuppressWarning(为了将@SuppressWarning限制在尽可能小的范围内)
@SuppressWarnings({"rawtypes","unchecked"})
Outer<T>.Inner casted = (Outer.Inner)o;
return casted;
编辑:好的,我相信我得到了它 - Eclipse的JDT在将Java代码传递给编译器之前对其进行解析 - 并且他们的解析器可以理解这样的强制转换,而(至少你的和我的javac
的版本不能。 (之后,Eclipse直接将解析后的代码传递给编译)。在提交错误之前,请查看最新版本的Java 6的行为。
答案 2 :(得分:1)
可悲的是,如果你是从Object
施放,那么就无法躲避未经检查的施法警告。那是因为Object
本身没有足够的类型信息来拥有T
。
(Java泛型是基于擦除的。因此无法在运行时知道对象是否具有类型参数T
---类型参数仅在编译时使用。)
答案 3 :(得分:0)
你不能将Object
投射到它不是的东西上。
答案 4 :(得分:0)
使用javac
编译时,以下“修复”有效。它也在Eclipse中成功编译。我认为的问题是你无法从变量中创建一个新的对象(就像你在你的情况下所做的那样)。我不知道如何解释它或验证我的理论。
/**
*
*/
package testcases;
/**
* @author The Elite Gentleman
*
*/
public class Outer<T> {
public class Inner{
}
public static <T> Outer<T>.Inner get(){
//Object o = new Object();
//return (Outer<T>.Inner)o;
return new Outer<T>().new Inner();
}
public static void main(String[] args) throws Exception {
Outer.<String>get();
}
}
基本上,由于Inner不是静态嵌套类,为了实例化它,你可以这样做:
new Outer<T>().new Inner();
此外,Object o = new Object();
并不保证对象o
实际上是Inner
类的实例。
更新我的解决方案帮助了对象实例化,而不是对现有实例化对象的对象转换。为此,我没有答案(但我们是开发人员,我们会想出一些东西:-))。
我能想到的是为什么不让Inner
类静态嵌套类?
答案 5 :(得分:0)
您的代码有两个问题:
首先是在编译时:您无法安全地将对象转换为泛型类型,因为泛型不是在Java中实现的。对于你的javac来说,转换为非实例化的泛型显然是危险的。我担心编译器对此的响应取决于编译器供应商和版本。
其次,将Object类型的Object转换为任何其他类型将在运行时抛出ClassCastException
,就像在Java中一样,类型信息包含在对象中,并且在创建后不会更改。
答案 6 :(得分:0)
如果必须使用强制转换,则可以使用@SuppressWarnings
来取消警告 Object o = new Object();
@SuppressWarnings("unchecked")
Outer<T>.Inner returnVal = (Outer.Inner) o;
return returnVal;
但是意识到警告的存在是因为你做了一些不安全的事情。对象不能转换为String。这将在运行时导致异常。
正如The Elite Gentleman注意到的那样,你可能希望将Inner标记为静态:
public static class Inner {
答案 7 :(得分:0)
(给出另一个答案,因为我之前的答案太吵了。)
为了将Java源代码编译为字节码,有两个重要的步骤:
当javac
编译时,它使用自己的解析器和字节码生成器(其实现细节将取决于您使用的JDK)。
当Eclipse的JDT编译时,它使用自己的代码解析器,之后......我不知道。我想说的是,无论如何,他们“绕过”javac
的一些解析器。 (例如,他们可以传递给javac修改过的java文件,用普通类替换所有普通类的转换)。
我的观点是 - 最后,javac
部分中的一个错误解析了java源代码。 (没有理由在语言规范中不允许这样的结构。)
为了抵消这个错误,您可以: *修改应用程序的设计以完全避免它。 *每次你必须进行此演员表时,每个人都会输入一个原始类型和一个@SuppressWarnings注释,而不是自然演员。