2013-02-26 27 views
16

Tôi đã nhìn thấy các câu hỏi tương tự nhưng chúng không giúp được gì nhiều.Cách lấy loại biến kiểu trong Java Generics

Ví dụ tôi đã có lớp này Generic:

public class ContainerTest<T> 
{ 

    public void doSomething() 
    { 
     //I want here to determinate the Class of the type argument (In this case String) 
    } 
} 

và Một lớp trong đó sử dụng lớp container này

public class TestCase 
{ 

    private ContainerTest<String> containerTest; 

    public void someMethod() 
    { 
     containerTest.doSomething(); 
    } 
} 

Có thể để quy kết lớp của đối số loại trong phương pháp doSomething() mà không cần có loại rõ ràng biến/trường hoặc bất kỳ số nào hàm tạo trong Lớp ContainerTest?

Cập nhật: Thay đổi định dạng của ContainerTest Lớp

+0

'if (t instanceof String)'? – vikingsteve

+0

Tôi đã cập nhật câu hỏi – ZeDonDino

+0

Bạn không thể chuyển loại lớp làm đối số? –

Trả lời

12

Cách duy nhất là để lưu trữ các lớp trong một biến dụ và yêu cầu nó như là một cuộc tranh cãi của các nhà xây dựng:

public class ContainerTest<T> 
{ 
    private Class<T> tClass; 
    public ContainerTest(Class<T> tClass) { 
     this.tCLass = tClass; 
    } 

    public void doSomething() 
    { 
     //access tClass here 
    } 
} 
+1

Đây chính xác là những gì tôi sẽ làm. –

+2

Bạn có chắc chắn đây là cách duy nhất không? – vach

+0

Trừ khi bạn buộc người gọi phải phân lớp của bạn, vâng. Nhưng nó thường không phải là một vấn đề ... –

5

Không có "sạch" cách để lấy đối số Generic Type từ bên trong lớp. Thay vào đó, một mô hình phổ biến là để vượt qua Class của Generic Type để constructor và giữ nó như là một tài sản bên trong juste như thực hiện trong việc thực hiện java.util.EnumMap.

http://docs.oracle.com/javase/1.5.0/docs/api/java/util/EnumMap.html http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/6-b14/java/util/EnumMap.java

public class ContainerTest<T> { 

    Class<T> type; 
    T t; 

    public ContainerTest(Class<T> type) { 
     this.type = type; 
    } 

    public void setT(T t) { 
     this.t = t; 
    } 

    public T getT() { 
     return t; 
    } 

    public void doSomething() { 
     //There you can use "type" property. 
    } 
} 
+0

Tại sao bạn liên kết EnumMap? – ZeDonDino

+0

Điều này được gọi là một 'loại mã thông báo' – hertzsprung

+1

@ZeDonDino Tôi liên kết EnumMap để cho thấy rằng ngay cả OpenJDK đang sử dụng mẫu này trong một triển khai standrad. – RenaudBlue

2

số Không thể vì type erasure (các thông số loại được biên dịch như Object + loại phôi). Nếu bạn thực sự cần biết/thực thi loại trong thời gian chạy, bạn có thể lưu trữ tham chiếu đến đối tượng Class.

public class ContainerTest<T> { 
    private final Class<T> klass; 
    private final List<T> list = new ArrayList<T>(); 

    ContainerTest(Class<T> klass) { 
    this.klass = klass; 
    } 

    Class<T> getElementClass() { 
    return klass; 
    } 

    void add(T t) { 
     //klass.cast forces a runtime cast operation 
     list.add(klass.cast(t)); 
    } 
} 

Sử dụng:

ContainerTest<String> c = new ContainerTest<>(String.class); 
+2

Sử dụng tính năng này một cách thận trọng, điều này chỉ được hỗ trợ Trong Java SE 7 trở lên. – ZeDonDino

+0

ContainerTest c = new ContainerTest (String.class) // và do đó viên kim cương được sinh ra – Javier

+0

Vâng, tôi biết điều này. Tôi hy vọng có một cách để đưa Class ra khỏi số và không chuyển giá trị vào một hàm tạo hoặc trường hoặc tương tự – ZeDonDino

0

Nếu Bạn có thể định nghĩa như thế này

public class ContainerTest<T> 
{ 

    public void doSomething(T clazz) 
    { 

    } 
} 

Sau đó, nó có thể

+0

Tôi muốn gọi phương thức này mà không có bất kỳ đối số nào. – ZeDonDino

0

một cách để có được những kiểu thời gian chạy của nhập tham số bằng cách sử dụng số TypeToken của Guava để chụp. Bất lợi của giải pháp là bạn phải tạo một phân lớp ẩn danh mỗi khi bạn cần một cá thể của Container.

class Container<T> { 

    TypeToken<T> tokenOfContainedType = new TypeToken<T>(getClass()) {}; 

    public Type getContainedType() { 
     return tokenOfContainedType.getType(); 
    } 
} 

class TestCase { 

    // note that containerTest is not a simple instance of Container, 
    // an anonymous subclass is created 
    private Container<String> containerTest = new Container<String>() {}; 

    @Test 
    public void test() { 
     Assert.assertEquals(String.class, containerTest.getContainedType()); 
    } 
} 

Mấu chốt của giải pháp này được mô tả trong tha javadoc của constructor TypeToken 's sử dụng trong đoạn code trên:

khách hàng tạo ra một lớp con nặc danh có sản phẩm nào. Làm như vậy sẽ nhúng tham số kiểu vào hệ thống phân cấp loại của lớp ẩn danh để chúng ta có thể tạo lại nó trong thời gian chạy mặc dù đã xóa.

+0

Wow, một downvote, đó là tốt đẹp ... Bạn có thể ít nhất thả một bình luận về lý do tại sao bạn không thích nó? – zagyi

+0

Đây là một giải pháp thú vị nhưng tôi nghĩ rằng nó sẽ phù hợp hơn cho phản xạ như ổi hơn là cho trường hợp sử dụng câu hỏi này. Vấn đề với giải pháp này là nó đòi hỏi phải tạo một lớp ẩn danh ở mỗi vị trí nơi bạn khởi tạo nó. (thông báo tôi không phải là người downvoter) –

8

Nếu bạn quan tâm trong phản ánh cách, tôi tìm thấy một giải pháp phần trong bài viết tuyệt vời này: http://www.artima.com/weblogs/viewpost.jsp?thread=208860

Nói tóm lại, bạn có thể sử dụng java.lang.Class.getGenericSuperclass()java.lang.reflect.ParameterizedType.getActualTypeArguments() phương pháp, nhưng bạn phải phân lớp một số cha mẹ siêu hạng.

Đoạn mã sau hoạt động cho một lớp học trực tiếp mở rộng siêu lớp AbstractUserType. Xem bài viết được tham chiếu để có giải pháp tổng quát hơn.

import java.lang.reflect.ParameterizedType; 


public class AbstractUserType<T> { 

    public Class<T> returnedClass() { 
     ParameterizedType parameterizedType = (ParameterizedType) getClass() 
       .getGenericSuperclass(); 

     @SuppressWarnings("unchecked") 
     Class<T> ret = (Class<T>) parameterizedType.getActualTypeArguments()[0]; 

     return ret; 
    } 

    public static void main(String[] args) { 
     AbstractUserType<String> myVar = new AbstractUserType<String>() {}; 

     System.err.println(myVar.returnedClass()); 
    } 

} 
+0

Điều này làm việc cho tôi. Cảm ơn. – aBarocio80

1

Để tìm hiểu giá trị của T bạn sẽ cần phải nắm bắt nó trong một định nghĩa kiểu bởi subclassing ContainerTest:

class MyStringClass extends ContainerTest<String> {} 

Bạn cũng có thể làm điều này với một lớp vô danh nếu bạn muốn:

ContainerTest<String> myStringClass = new ContainerTest<String>{}(); 

Sau đó, để giải quyết giá trị T, bạn có thể sử dụng TypeTools:

Class<?> stringType = TypeResolver.resolveRawArgument(ContainerTest.class, myStringClass.getClass()); 
assert stringType == String.class;