2010-02-23 29 views
21

Sự hiểu biết rõ ràng của tôi về Java Generics là cho đến bây giờ, loại Erasure loại bỏ tất cả các loại thông tin như vậy mà không có gì còn lại ở tất cả tại thời gian chạy. Gần đây, tôi tình cờ gặp một đoạn mã mà tôi phải tự hỏi bản thân mình: Cách thức hoạt động của nó? Giản, nó trình bày như sau:Tại sao không phải tất cả thông tin về loại được xóa trong Java khi chạy?

import java.lang.reflect.ParameterizedType; 
import java.lang.reflect.Type; 

public abstract class SuperClass<T> { 

    private final Type type; 

    protected SuperClass(){ 
     ParameterizedType parameterizedType = 
       (ParameterizedType) getClass().getGenericSuperclass(); 
     type = parameterizedType.getActualTypeArguments()[0]; 
    } 

    public void tellMyType(){ 
     System.out.println("Hi, my type parameter is " + type); 
    }  
} 

public class Example { 

    public static void main(String[] args) { 
     SuperClass sc = new SuperClass<Integer>(){}; 
     sc.tellMyType(); 
    } 
} 

Thi công các kết quả chính Class trong Hi, my type parameter is class java.lang.Integer.

Những gì chúng ta có thể thấy ở đây là thông tin loại T cũng có sẵn khi chạy, điều này mâu thuẫn với sự hiểu biết ban đầu của tôi.

Vì vậy, câu hỏi của tôi là: Tại sao trình biên dịch giữ điều này? Điều này có bắt buộc đối với một số hành vi JVM nội bộ hoặc có giải thích hợp lý nào về hiệu ứng này không?

Trả lời

16

Từ http://www.artima.com/weblogs/viewpost.jsp?thread=208860:

Nó chỉ ra rằng trong khi JVM sẽ không theo dõi các đối số loại thực cho trường hợp của một lớp học chung chung, nó không theo dõi các đối số loại thực cho lớp con của lớp generic . Trong Nói cách khác, trong khi một mới ArrayList<String>() thực sự chỉ là một mới ArrayList() lúc chạy, nếu một lớp kéo dài ArrayList<String> thì JVM biết rằng String là loại tranh luận thực tế cho List 's loại tham số.

Trong trường hợp của bạn, bạn đang tạo một lớp con ẩn danh của loại được tham số hóa, vì vậy thông tin loại được giữ lại. Xem bài viết để được giải thích chi tiết.

+0

Nhận xét ngắn gọn: nếu lớp con của bạn chỉ cần chuyển kiểu generic (như trong 'public class SuperList extends ArrayList '), đầu ra của mã ví dụ (không có bất kỳ lớp ẩn danh nào) sẽ là "Chào, tham số kiểu của tôi là T" – sfussenegger

+0

Vì vậy, nếu họ đã bỏ qua '{}' trong 'main' (có lẽ sẽ yêu cầu thay đổi constructor thành public), thì thực tế là tham số kiểu là' Integer' sẽ bị mất trong thời gian chạy? – MatrixFrog

1

This FAQ on type erasure có thể hữu ích.

+1

Bạn có kiểm tra xem nó có hoạt động không? – danben

+0

Làm thế nào tôi có thể biết nếu nó giúp Lars? –

14

Loại thông số đang được xoá hoàn toàn chỉ từ các loại động (tức là một loại đối tượng được tạo ra):

Object o = new ArrayList<String>(); // String erased 

Nó giữ lại trong các loại tĩnh (tức là lĩnh vực, lập luận và kiểu trả về, throws khoản, lớp cha và tờ khai superinterface):

class Test implements Superclass<String> { // String retained 
    // Accessible via Class.getGenericSuperclass() 

    private List<Integer> l; // Integer retained (via Field.getGenericType()) 

    public void test(List<Long> l) {} // Long retained (via Method.getGenericParameterTypes()) 

    // Character retained (via Method.getGenericReturnType()) 
    public List<Character> test() { return null; } 
} 

trong trường hợp của bạn, bạn tạo một lớp con nặc danh của SuperClass<Integer>, vì vậy tham số kiểu được giữ lại trong tờ khai lớp cha.

3

Google Guice sử dụng tính năng này để tạo TypeLiteral s để biểu diễn các lớp chung trong thời gian chạy. Ví dụ

TypeLiteral<List<String>> list = new TypeLiteral<List<String>>() {}; 

có thể được sử dụng, nhưng

Class<List<String>> list = List<String>.class; 

sẽ không biên dịch.

Kỹ thuật này được gọi là 'mã thông báo siêu loại' (xem Neal Gafter's article về chủ đề).

0

Tôi không thể nhận được ParameterizedType hoạt động cho tôi (Tôi không thể thay đổi mã SuperClass). Tuy nhiên, mã đơn giản sau đây hoạt động tốt:

try 
    { 
    SuperClass<Integer> castSc = (SuperClass<Integer>)sc; 
    // Do what you want to do if generic type is Integer... 
    } 
catch (ClassCastException ignored) 
    { 
    } 
Các vấn đề liên quan