2009-06-16 64 views
49

Nếu tôi đang tạo ra một lớp java để được chung chung, chẳng hạn như:Java Generic Class - Xác định Loại

public class Foo<T> 

Làm thế nào người ta có thể xác định trong nội bộ để lớp đó, những gì 'T' kết thúc lên được?

public ???? Bar() 
{ 
    //if its type 1 
    // do this 
    //if its type 2 
    // do this 
    //if its type 3 
    // do this 
    //if its type 4 
    // do this 
} 

Tôi đã thò quanh Java API và chơi với những thứ Reflection, instanceof, getClass, .class, vv, nhưng tôi có thể không có vẻ để làm cho người đứng đầu hoặc đuôi của chúng. Tôi cảm thấy như tôi gần gũi và chỉ cần kết hợp một số cuộc gọi, nhưng hãy tiếp tục ngắn gọn.

Để cụ thể hơn, tôi đang cố gắng xác định xem lớp học đã được khởi tạo với một trong 3 loại có thể.

+15

Mong muốn thực hiện điều này hầu như được bảo đảm là một lựa chọn thiết kế xấu. Rất có thể là câu lệnh if đòi hỏi kiến ​​thức về từng loại "" để được triển khai, vì vậy bạn có lẽ nên có tất cả các lớp đó có cùng kiểu gốc, và "làm điều này" phải là một phương thức ảo. Nó có thể liên quan đến các lớp học gói, nhưng tổng thể thiết kế của bạn sẽ cải thiện nếu bạn đi xuống con đường này. –

+2

Các khả năng trong các câu lệnh if là các trường hợp cạnh trong số tất cả các loại cuộc gọi có thể yêu cầu hoàn toàn việc triển khai riêng biệt hoàn toàn. Tôi đồng ý với những gì bạn đang nói ở đây, nhưng ứng dụng cụ thể là một tảng đá và một nơi khó khăn. –

+1

Sự cần thiết để có được các lớp cho một param loại chung chung không phải luôn luôn là một quyết định thiết kế xấu. Một yêu cầu hợp pháp (của tôi! :) có thể là một cuộc gọi phương thức yêu cầu tham số Class <>. – dahvyd

Trả lời

58

Tôi đã sử dụng một giải pháp tương tự như những gì ông giải thích ở đây cho một vài dự án và thấy nó khá hữu ích.

http://blog.xebia.com/2009/02/07/acessing-generic-types-at-runtime-in-java/

Các jist của nó được sử dụng như sau:

public Class returnedClass() { 
    ParameterizedType parameterizedType = (ParameterizedType)getClass() 
               .getGenericSuperclass(); 
    return (Class) parameterizedType.getActualTypeArguments()[0]; 
} 
+0

+1! tôi sẽ viết về điều này –

+0

+1 Và đây là giải pháp khá nhiều mà tôi đã đi với sau khi nghiên cứu thêm, vì vậy tôi đã thay đổi nó để trả lời chấp nhận của tôi. –

+0

+1 tuyệt vời! Tôi đã tìm kiếm nó trong nhiều giờ ... –

12

type erasure, không có cách nào để thực hiện việc này trực tiếp. Những gì bạn có thể làm, mặc dù, là vượt qua một Class<T> vào constructor và giữ nó bên trong lớp học của bạn. Sau đó, bạn có thể kiểm tra nó dựa trên ba loại có thể là Class mà bạn cho phép.

Tuy nhiên, nếu chỉ có ba loại có thể, bạn có thể muốn xem xét sắp xếp lại thành một enum thay thế.

+0

Điều này có vẻ giống như giải pháp tốt nhất, mặc dù ít hơn một lớp chung màu xanh thực sự. Cảm ơn các đầu vào. –

+0

Một lần nữa, tôi khuyên bạn nên mạnh mẽ xem xét đa hình hơn là một tuyên bố nếu dựa trên loại. –

+0

Thật không may là các mặt hàng trong xem xét không liên quan trong bất kỳ cách nào, do đó, một giải pháp đa hình là không thực tế. Các mục có thể rơi vào logic if-statement là các trường hợp cạnh của tất cả các xử lý 'bình thường' khác mà phải được xử lý hoàn toàn khác hoàn toàn, không có cách nào quan hệ. –

0

Toàn bộ vấn đề của một lớp chung là bạn không cần phải biết loại đang được sử dụng ....

+2

Đúng là đủ, tuy nhiên nếu tôi thiết kế một lớp có thể được khởi tạo một cách tổng quát về bất kỳ kiểu nào, tôi muốn xử lý một tập con cụ thể của tất cả các kiểu có thể như các trường hợp cạnh với logic cụ thể. –

+0

Sau đó tạo các loại phụ khác nhau. Hoặc ủy quyền cho các chiến lược (hoặc tương tự). –

3

Vấn đề là hầu hết các công cụ Generic sẽ biến mất trong quá trình biên dịch.

Một giải pháp phổ biến là lưu loại trong khi tạo đối tượng.

Đối với một giới thiệu ngắn trong hành vi Loại Erasure java đọc page

+1

Điều này rất hữu ích - Tôi đã nhìn thấy các giải pháp liên quan đến điều này và có thể sẽ đi tuyến đường này. –

+1

Có thể là thiết kế của bạn kém. –

0

Dường như những gì bạn muốn là trong thực tế không phải là một lớp Generic, nhưng một giao diện với một số hiện thực khác nhau. Nhưng có lẽ nó sẽ trở nên rõ ràng hơn nếu bạn nói mục tiêu cụ thể, thực tế của bạn.

44

Ngược lại với .NET Generics Java được triển khai bằng kỹ thuật được gọi là "loại tẩy xoá".

Điều này có nghĩa là trình biên dịch sẽ sử dụng thông tin loại khi tạo tệp lớp, nhưng không chuyển thông tin này sang mã byte. Nếu bạn xem các lớp được biên dịch bằng javap hoặc các công cụ tương tự, bạn sẽ thấy rằng List<String> là một đơn giản List (trong số Object) trong tệp lớp, giống như trong mã Java-5 trước.

Mã truy cập danh sách chung sẽ được "viết lại" bởi trình biên dịch để bao gồm các phôi bạn sẽ phải tự viết trong các phiên bản trước đó. Trên thực tế hai đoạn mã sau đây là giống hệt nhau từ góc độ mã byte khi trình biên dịch được thực hiện với họ:

Java 5:

List<String> stringList = new ArrayList<String>(); 
stringList.add("Hello World"); 
String hw = stringList.get(0); 

Java 1.4 và trước:

List stringList = new ArrayList(); 
stringList.add("Hello World"); 
String hw = (String)stringList.get(0); 

Khi đọc các giá trị từ một lớp chung trong Java 5 cần thiết cho tham số kiểu khai báo được chèn tự động. Khi chèn, trình biên dịch sẽ kiểm tra giá trị bạn cố gắng đặt vào và hủy bỏ với một lỗi nếu nó không phải là một String.

Toàn bộ điều đã được thực hiện để giữ cho các thư viện cũ và mã tổng hợp mới có khả năng tương tác mà không cần phải biên dịch lại các thư viện hiện có. Đây là một lợi thế lớn so với cách thức .NET, nơi các lớp chung và các lớp không chung chung sống song song nhưng không thể trao đổi một cách tự do.

Cả hai cách tiếp cận đều có ưu và nhược điểm của chúng, nhưng đó là cách nó có trong Java.

Để quay lại câu hỏi ban đầu: Bạn sẽ không thể nhận được thông tin loại lúc chạy, bởi vì nó đơn giản không còn ở đó nữa, khi trình biên dịch đã thực hiện công việc của mình. Điều này là chắc chắn hạn chế trong một số cách và có một số cách cranky xung quanh nó mà thường được dựa trên lưu trữ một trường hợp ở đâu đó, nhưng đây không phải là một tính năng tiêu chuẩn.

+2

+1 để biết thêm thông tin và độ tương phản với C# - cảm ơn bạn –

+1

Cảm ơn bạn. Tôi xin lỗi không có nút +10. –

0

Tôi đồng ý với Visage. Generics là để xác nhận thời gian biên dịch, không phải kiểu gõ động thời gian chạy. Âm thanh như những gì bạn cần thực sự chỉ là mẫu nhà máy. Nhưng nếu "làm điều này" không phải là sự khởi tạo, thì một Enum đơn giản có lẽ cũng sẽ hoạt động tốt. Giống như những gì Michael nói, nếu bạn có một ví dụ cụ thể hơn một chút, bạn sẽ nhận được câu trả lời tốt hơn.

+0

Tôi phải không đồng ý ở đây, và đã có một số đề cập đến một giải pháp không đa hình cần thiết trong không gian vấn đề cụ thể, và tôi đã khá hài lòng với những câu trả lời tôi nhận được. –

1

Nếu bạn biết một số loại cụ thể có ý nghĩa, bạn nên tạo các lớp con thuộc loại chung của bạn với việc triển khai.

Vì vậy

public class Foo<T> 

public ???? Bar() 
{ 
    //else condition goes here 
} 

Và sau đó

public class DateFoo extends Foo<Date> 

public ???? Bar() 
{ 
    //Whatever you would have put in if(T == Date) would go here. 
} 
Các vấn đề liên quan