用泛型投射到内部类

时间:2010-11-11 16:31:36

标签: java generics casting inner-classes

请考虑以下代码:

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

8 个答案:

答案 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源代码编译为字节码,有两个重要的步骤:

  1. 必须将源代码解析为语法树。
  2. 语法树用于generate bytecode
  3. javac编译时,它使用自己的解析器和字节码生成器(其实现细节将取决于您使用的JDK)。

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

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

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

相关问题