使用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中成功编译,但无法在javac
中javac
:
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源代码编译为字节码,有两个重要步骤:
- 必须将源代码解析为语法树。
- 语法树用于生成字节码 。
当javac
编译时,它使用自己的解析器和字节码生成器(其实现细节将取决于您使用的JDK)。
当Eclipse的JDT编译时,它使用自己的代码解析器,之后……我不知道。 我想说的是,无论如何,他们“绕过”了一些javac
的解析器。 (例如,他们可以传递给javac修改过的java文件,用普通类替换所有类通用类的转换)。
我的观点是 – 最后,它是javac
中解析java源代码的一个错误。 (没有理由在语言规范中不允许这样的结构。)
为了抵消该错误,您可以:*修改应用程序的设计以完全避免它。 *每次你必须进行这个演员表时,每个人都会输入一个原始类型和一个@SuppressWarnings注释,而不是自然演员。