使用generics投射到内部类

请考虑以下代码:

public class Outer { public class Inner{ } public static  Outer.Inner get(){ Object o = new Object(); return (Outer.Inner)o; } public static void main(String[] args) throws Exception { Outer.get(); } } 

此代码在Eclipse中成功编译,但无法在javacjavac

 Outer.java:10: ')' expected return (Outer.Inner)o; ^ Outer.java:10: ';' expected return (Outer.Inner)o; ^ Outer.java:10: illegal start of expression return (Outer.Inner)o; ^ 3 errors 

这是javac或Eclipse中的错误吗?

如果我将演员(Outer.Inner)o更改为(Outer.Inner)o它会编译,尽管有警告:

日食:

 Outer.Inner is a raw type. References to generic type Outer.Inner should be parameterized 

javac的:

 Outer.java:10: warning: [unchecked] unchecked conversion found : Outer.Inner required: Outer.Inner return (Outer.Inner)o; ^ 1 warning 

Javac版本: 1.6.0_21

我发现这是javac编译器中的一个错误,后来修复了。 JDK 7b100编译这个罚款。

请参见http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=6665356

最有趣的是,除非有一些我想念Javagenerics的东西,两者都是

 return (Outer.Inner) o; 

 return (Outer.Inner) o; 

两者都编译为相同的字节码。

第一行的问题发生在解析 – 意味着javac和Eclipse不对Java源代码使用相同的解析器。 你应该问一个关于Eclipse JDT的java解析器和javac之间存在什么差异的问题。 (或者在Eclipse上发布一个错误)。

如果你坚持保持这种行为(我建议将Inner重构为静态内部类),你可以使用带有字段分配的@SuppressWarning(为了将@SuppressWarning限制在尽可能小的范围内)。

 @SuppressWarnings({"rawtypes","unchecked"}) Outer.Inner casted = (Outer.Inner)o; return casted; 

编辑 :好的,我相信我得到了它 – Eclipse的JDT在将它传递给编译器之前解析Java代码 – 并且他们的解析器可以理解这样的演员,而(至少你和我的版本) javac不能。 (之后,Eclipse直接将解析后的代码传递给编译)。 在提交错误之前,请查看最新版本的Java 6的行为方式。

可悲的是,如果你是从一个Object施放,那么你就无法避开未经检查的施法警告。 那是因为Object本身没有足够的类型信息来拥有T

(Javagenerics是基于擦除的。因此无法知道对象在运行时是否具有类型参数T —类型参数仅在编译时使用。)

你不能将一个Object为它不是的东西。

您的代码有两个问题:

首先是在编译时:您无法安全地将对象转换为generics类型,因为generics不是在Java中实现的。 对于你的javac来说,转换为非实例化的generics显然是危险的。 我担心编译器对此的响应取决于编译器供应商和版本。

其次,将Object类型的Object转换为任何其他类型将在运行时抛出ClassCastException ,就像在Java中一样,类型信息包含在对象中,并且在创建后不会更改。

当我使用javac编译时,以下“修复”工作。 它也在Eclipse中成功编译。 我认为的问题是你无法从变量中创建一个新的对象(就像你在你的情况下所做的那样)。 我不知道如何解释它或validation我的理论。

 /** * */ package testcases; /** * @author The Elite Gentleman * */ public class Outer { public class Inner{ } public static  Outer.Inner get(){ //Object o = new Object(); //return (Outer.Inner)o; return new Outer().new Inner(); } public static void main(String[] args) throws Exception { Outer.get(); } } 

基本上,由于Inner不是静态嵌套类,为了实例化它,你可以这样做:

 new Outer().new Inner(); 

另外, Object o = new Object(); 不保证对象o实际上是Inner类的实例


更新我的解决方案有助于Object实例化,而不是对现有实例化对象的对象转换。 为此,我没有答案(但我们是开发人员,我们会想出一些事情:-))。

我能想到的是为什么不制作Inner类静态嵌套类?

如果必须使用强制转换,则可以使用@SuppressWarnings禁止警告

  Object o = new Object(); @SuppressWarnings("unchecked") Outer.Inner returnVal = (Outer.Inner) o; return returnVal; 

但是意识到警告的存在是因为你做了一些不安全的事情。 对象不能转换为String。 这将在运行时导致exception。

正如The Elite Gentleman注意到的那样,你可能希望将Inner标记为静态:

 public static class Inner { 

(给出另一个答案,因为我之前的那个太吵了。)

要将Java源代码编译为字节码,有两个重要步骤:

  1. 必须将源代码解析为语法树。
  2. 语法树用于生成字节码 。

javac编译时,它使用自己的解析器和字节码生成器(其实现细节将取决于您使用的JDK)。

当Eclipse的JDT编译时,它使用自己的代码解析器,之后……我不知道。 我想说的是,无论如何,他们“绕过”了一些javac的解析器。 (例如,他们可以传递给javac修改过的java文件,用普通类替换所有类通用类的转换)。

我的观点是 – 最后,它是javac中解析java源代码的一个错误。 (没有理由在语言规范中不允许这样的结构。)

为了抵消该错误,您可以:*修改应用程序的设计以完全避免它。 *每次你必须进行这个演员表时,每个人都会输入一个原始类型和一个@SuppressWarnings注释,而不是自然演员。