2012-02-16 50 views
11

xem xét như sau:Java Generics - loại suy luận

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"); 
    } 
} 

It in Object: abc. Tại sao nó không in chuỗi: abc?

+0

xem câu hỏi này vào việc loại T - [Get Generic Loại Lớp tại Runtime] (http: // stackoverflow .com/questions/3403909/get-generic-type-of-class-at-runtime) – csturtz

Trả lời

4

Ghi đè phương thức hỗ trợ Java (ràng buộc kiểu động), nhưng không phải những gì bạn đang cố gắng đạt được (quá tải là tính đa hình tĩnh và không động).

Để đạt được những gì bạn muốn đạt được trong Java, bạn cần công văn kép.

Mẫu khách truy cập phải là bạn của bạn ở đây.

Tôi đã viết cho bạn một mẫu mã.

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

Vì Generics Generics không mang tính tổng quát theo cách bạn nghĩ. Khi mã java chung được biên dịch tất cả các thông tin kiểu thực sự bị tước đi, và chỉ loại cơ sở được biết vẫn còn. Trong trường hợp này, loại đó là Object.

Generics trong java thực sự chỉ là thủ thuật biên dịch, nơi trình biên dịch loại bỏ các phôi mà nếu không sẽ là cần thiết và gây ra một hạn chế thời gian biên dịch. Cuối cùng, tất cả những gì còn lại là kiểu cơ sở khi điều này thực sự được biên dịch thành mã byte.

Quá trình này được gọi là loại tẩy xóa. Điều này previous question là hữu ích để hiểu những gì thực sự xảy ra.

0

Bởi vì nó có thể biết rằng chỉ trên thời gian chạy, nhưng trong thực tế, vì java là một ngôn ngữ được biên dịch chứ không phải là một ngôn ngữ kịch bản, nó đang được quyết định về thời gian biên dịch.

Generics java cho phép "một loại hoặc phương thức hoạt động trên các đối tượng thuộc nhiều loại khác nhau trong khi cung cấp thời gian biên dịch loại an toàn".

Bạn có thể tất nhiên thử một cái gì đó như:

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

mặc dù nó không phải là những gì bạn đang sau, đó là không thể vì trình biên dịch đang kêu gọi các mũi chích ngừa.

10

Đây là vì Java type erasure:

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

của bạn thực sự là một cú pháp đường trên đầu trang của

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

Để công bằng, rằng "cú pháp đường" cho phép trình biên dịch làm một số rất đẹp và kiểm tra quan trọng, nhưng tại thời gian chạy chỉ có một bản sao của phương pháp printWithClass và nó sử dụng java.lang.Object làm loại biến của bạn t.

Nếu bạn đã có kinh nghiệm về Generics trong các ngôn ngữ khác (C#, C++ templates, Ada) loại xóa sẽ trái ngược với những gì bạn biết, nhưng đây là cách nó hoạt động dưới trang bìa.

+0

Nó không phải về loại tẩy xoá, đó là về các giới hạn chung có sẵn _at biên dịch time_. Nếu chúng ta sử dụng '', đầu ra là 'String: abc'. –

+1

@Daniel tuyệt đối - bởi vì phương thức sẽ trở thành 'static void printWithClass (String t)' sau đó. Nhưng nó sẽ vẫn là một phương thức duy nhất, với một kiểu duy nhất, bị ràng buộc tại thời gian biên dịch để gọi một quá tải đơn của phương thức 'print' tĩnh. – dasblinkenlight

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

sẽ được comipled để

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

Generics được giải thích bởi trình biên dịch và họ thực thi thêm séc loại để tránh bất kỳ vấn đề đúc runtime. Thông tin Loại Chung là lost at runtime. Vì vậy, tại thời gian chạy những gì printWithClass nhận được chỉ là đối tượng chứ không phải String và do đó kết quả của bạn.

4

Nó không phải là loại xóa, đó là một vấn đề biên dịch và điều tương tự sẽ xảy ra nếu JVM lưu trữ phương thức generics khi chạy. Nó cũng không phải về suy luận kiểu - trình biên dịch sẽ nhập <String> như bạn mong đợi.

Vấn đề là khi trình biên dịch tạo mã cho printWithClass, cần có chữ ký phương thức cụ thể để liên kết với cuộc gọi print. Java không có nhiều công văn, vì vậy nó không thể đặt một chữ ký mơ hồ trong bảng phương thức và quyết định những gì cần gọi khi chạy. Giới hạn trên duy nhất trên TObject, do đó, phương thức duy nhất phù hợp là print(Object).

0

thêm ví dụ để làm rõ:

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)); 
    } 
} 

này in:

Number: 1234 
Các vấn đề liên quan