2012-02-16 150 views
11

考虑以下几点:Java泛型 - 类型推断

public class GenericTest { 
    static void print(int x) { 
     System.out.println("Int: " + x); 
    } 
    static void print(String x) { 
     System.out.println("String: " + x); 
    } 

    static void print(Object x) { 
     System.out.println("Object: " + x); 
    } 

    static <T> void printWithClass(T t) { 
     print(t); 
    } 
    public static void main(String argsp[]) { 
     printWithClass("abc"); 
    } 
} 

它打印对象:ABC。 为什么不打印字符串:abc?

+0

看到在得到T的类型这个问题 - [在运行获取类通用类型](HTTP://计算器.com/questions/3403909/get-generic-of-class-at-runtime) – csturtz 2012-02-16 14:39:06

回答

4

Java支持方法重写(动态类型绑定),但不是你想要实现的(重载是静态多态而不是动态的)。

为了实现你想在Java中实现的目标,你需要双重调度。

访客模式应该是你的朋友在这里。

我写过你的代码示例。

public class Test { 

    public static void main(String argsp[]) { 
     PrintTypeImpl typeImpl = new PrintTypeImpl(new StringType(), new IntType(), new ObjectType()); 
     typeImpl.accept(new PrintVisitor()); 
    } 

    static final class PrintVisitor implements TypeVisitor { 
     public void visit(IntType x) { 
      System.out.println("Int: "); 
     } 

     public void visit(StringType x) { 
      System.out.println("String: "); 
     } 

     public void visit(ObjectType x) { 
      System.out.println("Object: "); 
     } 
    } 

    interface TypeVisitor { 
     void visit(IntType i); 

     void visit(StringType str); 

     void visit(ObjectType obj); 
    } 

    interface PrintType { 
     void accept(TypeVisitor visitor); 
    } 

    static class StringType implements PrintType { 
     @Override 
     public void accept(TypeVisitor visitor) { 
      visitor.visit(this); 
     } 
    } 

    static class ObjectType implements PrintType { 
     @Override 
     public void accept(TypeVisitor visitor) { 
      visitor.visit(this); 
     } 
    } 

    static class IntType implements PrintType { 
     @Override 
     public void accept(TypeVisitor visitor) { 
      visitor.visit(this); 
     } 
    } 

    static final class PrintTypeImpl implements PrintType { 

     PrintType[] type; 

     private PrintTypeImpl(PrintType... types) { 
      type = types; 
     } 

     @Override 
     public void accept(TypeVisitor visitor) { 
      for (int i = 0; i < type.length; i++) { 
       type[i].accept(visitor); 
      } 
     } 
    } 

} 
0

因为java泛型不是您认为的泛型方式。当通用java代码被编译时,所有类型的信息实际上被剥离,只有基本已知类型保留。在这种情况下,该类型是Object

java中的泛型实际上只是编译器的诡计,编译器会删除否则会导致编译时间受到限制的转换。最后,剩下的就是基本类型,这实际上被编译成字节码。

该过程被称为类型擦除。这previous question有助于了解实际发生的情况。

0

因为它只能在运行时才知道,但实际上,因为java是一种编译语言而不是脚本语言,所以它正在编译时决定。

java泛型允许“一种类型或方法操作各种类型的对象,同时提供类型安全的编译时间”。

当然,你可以尝试这样的:

static <T extends String> void printWithClass(T t) { 
    print(t); 
} 

虽然这不是你以后,因为编译器发号施令这是不可能的。

10

这是因为Java type erasure:你

static <T> void printWithClass(T t) { 
    print(t); 
} 

实际上是对

static void printWithClass(Object t) { 
    print(t); 
} 

顶部的语法糖说句公道话,说:“语法糖”让编译器做了一些非常好的,重要的检查,但在运行时只有一个printWithClass方法的副本,它使用java.lang.Object作为变量的类型t

如果您有其他语言(C#,C++模板,Ada)的泛型经验与您所知道的相反,但这是它在封面下的工作原理。

+0

这不是关于类型擦除,而是关于在编译时可用的通用边界。如果我们使用'',则输出是'String:abc'。 – 2012-02-16 14:47:08

+1

@Daniel绝对 - 因为这个方法会变成'static void printWithClass(String t)'。但它仍然是一个单一的方法,只有一种类型,在编译时绑定来调用静态'print'方法的一个重载。 – dasblinkenlight 2012-02-16 14:49:53

0
static <T> void printWithClass(T t) { 
    print(t); 
} 

将comipled到

static void printWithClass(Object t) { 
    print(t); 
} 
0

泛型编译器的解释和执行这些额外的类型检查,以避免任何运行时铸造的问题。通用类型信息是lost at runtime。所以在运行时printWithClass接收的只是对象而不是String,因此也是你的结果。

4

这不是关于类型擦除,它是一个编译问题,如果JVM在运行时存储方法泛型,会发生同样的情况。这也与类型推断无关 - 编译器按照您的预期推断<String>

问题是当编译器生成代码为printWithClass时,它需要一个特定的方法签名才能与print调用相关联。 Java没有多次调度,所以它不能在方法表中放置模糊的签名并决定在运行时调用什么。 T唯一的上限是Object,所以匹配的唯一方法是print(Object)

0

额外例子来阐明:

public class OverloadingWithGenerics { 

    static void print(Integer x) { 
     System.out.println("Integer: " + x); 
    } 

    static void print(Double x) { 
     System.out.println("Double: " + x); 
    } 

    static void print(Number x) { 
     System.out.println("Number: " + x); 
    } 

    static <T extends Number> void printWithClass(T t) { 
     print(t); 
    } 

    public static void main(String argsp[]) { 
     printWithClass(new Integer(1234)); 
    } 
} 

此打印:

Number: 1234