2016-04-06 24 views
15

Tôi đang cố gắng tạo trình bao bọc trên Retrofit để trừu tượng triển khai dịch vụ của mình. Tôi đã nhận được biên dịch để biên dịch thành công cho đến nay:Chuyển lớp lồng nhau <MyInterface <T>> làm tham số trong Android

package com.example.spark.testapp.services; 

import com.example.spark.testapp.services.apis.Get; 
import com.example.spark.testapp.services.apis.Post; 
import com.example.spark.testapp.services.utils.*; 
import com.example.spark.testapp.services.utils.Error; 

import java.util.List; 

import retrofit2.Call; 
import retrofit2.Callback; 
import retrofit2.Response; 
import retrofit2.Retrofit; 




public class ServiceLayer { 
    public <T> void performGet(String url, final Class<Get<T>> clazz, com.example.spark.testapp.services.utils.Callback<T> callback) { 
     Retrofit retrofit = new Retrofit.Builder().baseUrl("").build(); 
     Get<T> service = retrofit.create(clazz); 
     //Pass authentication token here 
     Call<T> t = service.get(url, ""); 
     executeCallback(callback,t); 
    } 

    public <T> void performPost(String url, final Class<Post<T>> clazz,com.example.spark.testapp.services.utils.Callback<T> callback) { 
     Retrofit retrofit = new Retrofit.Builder().baseUrl("").build(); 
     Post<T> service = retrofit.create(clazz); 

     //Pass authentication token here 
     Call<T> t = service.post(url, ""); 
     executeCallback(callback,t); 
    } 

    public <T> void executeCallback(final com.example.spark.testapp.services.utils.Callback<T> callback , Call<T> call) { 
     call.enqueue(new Callback<T>() { 
      @Override 
      public void onResponse(Call<T> call, Response<T> response) { 
       callback.onSuccess(response.body()); 
      } 


      @Override 
      public void onFailure(Call<T> call, Throwable t) { 
       ///Find out what exactly went wrong. Populate Error. and then... 
       com.example.spark.testapp.services.utils.Error e = new Error(); 
       callback.onFailure(e); 
      } 
     }); 
    } 
} 

Trong khi điều này biên dịch, vấn đề là tại thời điểm gọi phương thức:

private void getString() { 

     ServiceLayer s = new ServiceLayer(); 
     s.performGet("",Get<String>.class,this); //Cannot select from parameterised type 

    } 

Tôi đã google xung quanh này một chút và phát hiện ra rằng điều này là không thể do loại tẩy xoá. Khỏe.

Nhưng câu hỏi của tôi là, trình biên dịch có nên nêu ra lỗi ở đây không? Tại dòng này? :

public <T> void performGet(String url, final Class<Get<T>> clazz, com.example.spark.testapp.services.utils.Callback<T> callback) 

Lớp dịch vụ của tôi được biên dịch như thế nào?

EDIT

Câu hỏi đặt ra dường như bị hiểu lầm. Tôi không tìm cách để thiết kế này hoạt động. Tôi hiểu lỗ hổng trong đó và chúng tôi đã tìm thấy một cách tốt hơn để lớp dịch vụ của mình. Câu hỏi đặt ra là về hành vi thú vị/kỳ lạ của chính ngôn ngữ đó.

+0

Tôi giả định 'this' thực hiện 'Gọi lại '? – njzk2

+0

có liên quan: http://stackoverflow.com/questions/27000227/cannot-select-parameterized-type – njzk2

+0

@ njk2 - Có. Bạn đúng rồi. 'this' thực hiện gọi lại . Và nó không liên quan chút nào. Câu hỏi đặt ra là, lớp dịch vụ của tôi được biên dịch như thế nào nếu bạn không thể gọi nó? – avismara

Trả lời

0

Đã thực hiện một số thử nghiệm và tìm thấy một số giải pháp, hy vọng nó sẽ hữu ích.

Kể từ khi mã này trả về true (trong đó A là một số lớp chung mà không làm gì cả):

A<String> a = new A<String>(); 
System.out.println(A.class == a.getClass()); 

Nó có nghĩa là bạn không cần phải nhận được lớp của 'Get < T>' nhưng chỉ 'Get 'cho Retrofit bạn

gì bạn cần là để gửi T như là đối số khác, đối với chức năng để biết T đây là những gì, ví dụ:

public <T> void performGet(String url, final Class<Get> clazz, final Class<T> t, com.example.spark.testapp.services.utils.Callback<T> callback) { 
    Retrofit retrofit = new Retrofit.Builder().baseUrl("").build(); 
    Get<T> service = retrofit.create(clazz); 
    ... 
+0

Chúng tôi đã thử giải pháp này trước đây, nhưng chúng tôi không muốn thông tin kiểu 'T' bị mất trong' retrofit.create (clazz) '. Những gì nó trả về là một cái gì đó của loại 'Get' không' Nhận '. – avismara

+0

Theo loại bạn có nghĩa là lớp học? Nếu có ví dụ đầu tiên cho thấy 'Get ' và 'Get' là của cùng một lớp. Nếu không, bạn có thể cho biết loại lỗi nào u nhận được sau khi thiết lập nó bằng 'Get = ...'? –

+0

Theo loại, ý tôi là loại. Tôi không nhận được lỗi, nhưng nó cho tôi một cảnh báo, một cách tự nhiên. Kiểu 'Get ' và 'Get' không giống nhau. Ngay cả khi chúng tôi làm điều này theo cách bạn đề xuất, nó sẽ biên dịch, nhưng nó chắc chắn sẽ không làm những gì tôi muốn nó làm. :) Ngoài ra, đây không phải là câu trả lời cho câu hỏi của tôi. – avismara

-1

Nếu bạn muốn giữ lại các chung thông số lớp (T) thông tin có thể truy cập trong một phương thức, bạn phải cung cấp thông tin lớp. Ví dụ:

public class SomeClass<T, X, C> { 
    Class<X> xClass; 
    Class<T> tClass; 
    Class<C> cClass; 

    public SomeClass<T, X, C>(Class<T> a, Class<X> b, Class<C> c){ 
     tClass = a; 
     xClass = b; 
     cClass = c; 
    } 
} 

Bằng cách đó, bạn có thể sử dụng các thông tin lớp bên trong lớp "người tiêu dùng", hoặc là để truy cập vào chữ ký thông thường, quy trình serialization/deserialization, tạo đối tượng, phương pháp gọi chung vv

Bây giờ , nếu bạn chỉ muốn sử dụng thông tin của thông số lớp chung trong một phương thức, bạn nên:

public <T> void performGet(String url, final Class<T> clazz, com.example.spark.testapp.services.utils.Callback<T> callback) { 
    Retrofit retrofit = new Retrofit.Builder().baseUrl("").build(); 
    Get<T> service = retrofit.create(clazz); 
    //Pass authentication token here 
    Call<T> t = service.get(url, ""); 
    executeCallback(callback,t); 
} 

Điều này cho phép bạn lưu thông tin lớp chung vào lớp chứa phương pháp; bạn có thể truy cập nó trong phương thức của bạn và sử dụng nó như một tham số cho các phương thức chung khác và mọi thứ khác.

Nếu bạn cần thêm ví dụ, hãy xem xét điều này. Đó là một wrapper quá, nhưng xung quanh Spring RestTemplate-Android, vì vậy có lẽ bạn có thể nhận được một số ý tưởng từ nó. (Disclaimer - Tôi teh dev)

https://github.com/fcopardo/EasyRest/blob/master/src/main/java/com/grizzly/rest/GenericRestCall.java

+0

Vui lòng xem bản chỉnh sửa. Tôi không tìm kiếm một giải pháp để làm việc này. Tôi hỏi tại sao trình biên dịch lại cho phép biên dịch mã này --- một mã mà chữ ký phương thức là không thể gọi được "--- khi nó không nên. Câu trả lời này, như thông tin như nó, không thực sự là những gì tôi tìm kiếm – avismara

4

Nhưng câu hỏi của tôi là, không nên trình biên dịch nâng cao một lỗi ở đây?

Chữ ký phương pháp hoàn toàn chính xác trong Java. Và chữ ký phương thức chung được kiểm soát bởi các quy tắc giống như các phương thức thông thường.

Trong một phương pháp chung, các hành động mà bạn có thể thực hiện trong mã phụ thuộc vào loại tham số. Ví dụ, nếu bạn có phương pháp này:

public static <T> void test(List<T> list, Class<T> clazz) 
     throws InstantiationException, IllegalAccessException 
{ 
    list.add(clazz.newInstance()); 
} 

Nó biên dịch, nhưng nếu chúng ta thêm dòng tiếp theo:

list.add(new Integer(1)); 

nó sẽ không bởi vì trong thời gian biên dịch list chỉ chấp nhận trường hợp của T. Do đó phương pháp chung được xác định rõ.

Khi bạn cố gọi phương thức chung của mình, trình biên dịch không thể suy ra được T từ các tham số. Vấn đề chính rõ ràng là cấu trúc Class<Get<T>>, có giá trị trong chữ ký phương thức nhưng không được đề xuất. Mặc dù, bạn có thể làm một diễn viên không an toàn và lạ để thực hiện cuộc gọi phương pháp biên dịch và làm việc:

s.performGet("",(Class<Get<String>>)(Class) Get.class,this); 

Làm chuỗi diễn viên này ngay bây giờ trình biên dịch có thể suy ra T, bởi vì các loại generic chỉ được kiểm tra trong thời gian biên dịch. Trong thời gian chạy, Class<Get<T>> sẽ luôn là Get.class.

Một số câu hỏi liên quan về chủ đề này:

Passing the Class<T> in java of a generic list?

Generic type as parameter in Java Method

1

Đầu tiên, cú pháp của bạn là hợp lệ, do đó trình biên dịch không dhows lỗi trong dòng nhất định. Lỗi mà bạn có có thể liên quan đến rằng Class là lớp đặc biệt được tạo bởi VM và trình biên dịch trong khi kiểm tra lỗi xử lý nó như là lớp bình thường.

Hãy nhìn vào ví dụ này:

class Get<T> { 
    } 

class Callback<T> { 
    } 
class Clazz<T> { 
    } 

static class ServiceLayer { 

     public <T> void performGet(String url, final Clazz<Get<T>> clazz, 
       Callback<T> callback) { 
     } 
    } 

bây giờ nếu bạn cố gắng gọi

ServiceLayer a = new ServiceLayer(); 
    Clazz<Get<String>> clazz = new Clazz<Hello.Get<String>>(); 
    Callback<String> callback = new Callback<String>(); 
    a.performGet("someurl", clazz, callback); 

bạn muốn có bất kỳ vấn đề. Vì vậy, như bạn thấy không có vấn đề với cú pháp đó, nhưng với đối tượng đặc biệt.

Các vấn đề liên quan