Câu trả lời ngắn gọn là những gì bạn cần để di chuyển tạo ra TypeToken
ra khỏi Executor
, ràng buộc T
trong Response<T>
khi bạn tạo các token (new TypeToken<Response<Language>>() {}
), và vượt qua trong các loại thẻ để các nhà xây dựng Executor
.
Câu trả lời dài là:
Generics trên một loại thường được xoá hoàn toàn trong thời gian chạy, trừ khi loại là biên soạn với tham số chung ràng buộc. Trong trường hợp đó, trình biên dịch sẽ chèn thông tin kiểu chung vào lớp được biên dịch. Trong các trường hợp khác, điều đó là không thể.
Vì vậy, ví dụ, hãy xem xét:
List<Integer> foo = new ArrayList<Integer>();
class IntegerList extends ArrayList<Integer> { ... }
List<Integer> bar = new IntegerList();
Khi chạy, Java biếtbar
chứa số nguyên vì loại Integer
là ràng buộc để ArrayList
tại thời gian biên dịch, vì vậy những thông tin kiểu generic được lưu trong lớp IntegerList
tập tin. Tuy nhiên, thông tin loại chung cho foo
bị xóa, do đó, trong thời gian chạy nó không thực sự có thể xác định rằng foo
được cho là chứa Integer
s. Vì vậy, thường xuất hiện rằng chúng ta cần thông tin kiểu chung trong một tình huống mà thông thường sẽ bị xóa trước khi chạy, chẳng hạn như ở đây trong trường hợp phân tích cú pháp dữ liệu JSON trong GSON.Trong những tình huống này, chúng ta có thể tận dụng thông tin loại được giữ nguyên khi biên dịch tại thời điểm biên dịch (như trong ví dụ IntegerList
ở trên) bằng cách sử dụng type tokens, đây thực sự là các lớp ẩn danh nhỏ, thuận tiện lưu trữ thông tin loại chung.
Bây giờ để mã của bạn:
Type responseType = new TypeToken<Response<T>>() {}.getType();
Trong dòng này của lớp Executor
bạn, chúng tôi tạo ra một lớp vô danh (kế thừa từ TypeToken
) trong đó có các loại Response<T>
cứng mã hoá (ràng buộc) tại thời gian biên dịch. Vì vậy, trong thời gian chạy, GSON có thể xác định rằng bạn muốn một đối tượng của Response<T>
. Nhưng nó không biết những gì T
là, bởi vì bạn đã không xác định nó tại thời gian biên dịch! Vì vậy, GSON không thể xác định loại nào sẽ ở trong các đối tượng List
của đối tượng Response
mà nó tạo và thay vào đó, chỉ cần tạo một số StringMap
.
Đạo đức của câu chuyện là bạn cần chỉ định rằng T
lúc biên dịch. Nếu Executor
có nghĩa là được sử dụng chung, bạn có thể cần phải tạo mã thông báo loại bên ngoài lớp đó, trong mã máy khách của bạn. Một cái gì đó như:
class Executor<T> {
private TypeToken<Response<T>> responseType;
private Response<T> response;
public Executor(TypeToken<Response<T>> responseType) {
this.responseType = responseType;
}
public void execute() {
this.response = new Gson().fromJson(json, responseType.getType());
}
public Response<T> getResponse() { return this.response; }
}
// client code:
Executor<Language> executor = new Executor<Language>(new TypeToken<Response<Language>>() { });
executor.execute();
List<Language> languages = executor.getResponse().getData();
System.out.println(languages.get(0).alias); // prints "be"
Nhân tiện, tôi đã thử nghiệm ở trên trên máy tính của mình.
Xin lỗi nếu quá lâu!
Bạn có chắc chắn ngoại lệ xảy ra ở dòng này không? Tôi không thể thấy nơi execute() được gọi trong mã mẫu mà bạn cung cấp. –
@SamuelEUSTACHI Ồ, tệ của tôi. Câu hỏi chỉnh sửa, nó trượt ra khi tôi dán mã (nó là một chút đơn giản). Có, tôi chắc chắn 100%, nó xảy ra chính xác tại dòng đó (ít nhất theo stacktrace). – Zar
@Zar executor = new Executor
Visruth