2013-06-07 22 views
7

Tôi có một vấn đề "lạ" về Generics Java.Phương thức của lớp chung Java không áp dụng cho các đối số được chuyển qua

Trước tiên tôi liệt kê mã của tôi:

Service.class

package jse.generics.service; 

public interface Service { 

} 

ServiceProvider.class

package jse.generics.service; 

public interface ServiceProvider<T extends Service> { 

    public T getService(); 
} 

ServiceProviderRegistry.class

package jse.generics.service; 

import java.util.HashMap; 
import java.util.Map; 

public class ServiceProviderRegistry<T extends Service> { 

    private Map<Class<T>, ServiceProvider<T>> map = new HashMap<Class<T>, ServiceProvider<T>>(); 

    public void register(Class<T> clazz, ServiceProvider<T> provider) { 
      map.put(clazz, provider); 
    } 
} 

FooService.class

package jse.generics.service; 

public class FooService implements Service { 

} 

FooServiceProvider.class

package jse.generics.service; 

public class FooServiceProvider implements ServiceProvider<FooService> { 

    @Override 
    public FooService getService() { 
      return new FooService(); 
    } 

} 

ServiceTest.class

package jse.generics.service; 

public class ServiceTest { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
      ServiceProviderRegistry<? extends Service> registry = new ServiceProviderRegistry<Service>(); 
      registry.register(FooService.class, new FooServiceProvider()); 
    } 

} 

Trong lớp ServiceTest, trình biên dịch phàn nàn rằng phương thức registry.register không áp dụng cho các đối số được truyền cho nó. Tôi thực sự không biết tại sao điều này xảy ra. Vì vậy, tôi rất mong được bạn giúp đỡ để giải quyết vấn đề này.

+0

puhhh ... xin vui lòng định dạng mô tả của bạn ... :-) –

+0

ở đâu 'T' tuyên bố trong 'ServiceProvider.getService()'? – hertzsprung

Trả lời

5

Trong trường hợp này, bạn sẽ được tốt hơn nếu ServiceProviderRegistrykhông một lớp tham số, nhưng thay vì làm cho register phương pháp (và có lẽ là phương pháp tra cứu tương ứng) generic. Trong cách tiếp cận hiện tại của bạn, ServiceProviderRegistry<Service> chỉ có thể chỉ đăng ký Service.class, không phải bất kỳ lớp con nào của Dịch vụ.

Tất cả những gì bạn thực sự quan tâm là lớp và nhà cung cấp được chuyển đến register khớp với nhau, đây là trường hợp lý tưởng cho phương pháp chung.

public class ServiceProviderRegistry { 
    private Map<Class<?>, ServiceProvider<?>> registry = new HashMap<>(); 

    public <T extends Service> void register(Class<T> cls, ServiceProvider<T> provider) { 
    registry.put(cls, provider); 
    } 

    @SuppressWarnings("unchecked") 
    public <T extends Service> ServiceProvider<T> lookup(Class<T> cls) { 
    return (ServiceProvider<T>)registry.get(cls); 
    } 
} 

Bạn sẽ cần @SuppressWarnings chú thích - nó không thể thực hiện mô hình này mà không có một trong một cách mà hoàn toàn sẽ làm hài lòng các trình biên dịch, mà chỉ có quyền truy cập vào các loại thời gian biên dịch. Trong trường hợp này, bạn biết rằng diễn viên sẽ luôn an toàn khi chạy vì register là điều duy nhất sửa đổi bản đồ registry, do đó, @SuppressWarnings là hợp lý. Jon Skeet's answer to this related question tổng hợp nó rất độc đáo

Đôi khi Java Generics không cho phép bạn thực hiện những gì bạn muốn, và bạn cần phải có hiệu quả nói với trình biên dịch rằng bạn đang thực sự hợp pháp vào thời gian thực hiện.

+0

'HashMap <>() mới chỉ hoạt động cho Java 7 trở lên. Đối với Java 6 trở xuống, bạn cần 'HashMap mới , ServiceProvider >()' –

+0

Ngoài ra, phương thức 'register' của bạn thiếu kiểu trả về' void'. –

+0

@RajeshJAdvani cảm ơn, tôi đổ lỗi cho quá nhiều lập trình trong groovy :-) –

5

Phương pháp chữ ký là

public void register(Class clazz, ServiceProvider provider) 

Bạn đang đi qua hai Class dụ ở đây:

registry.register(FooService.class, FooServiceProvider.class); 

Bạn cần phải vượt qua một thể hiện của một lớp mà thực hiện giao diện ServiceProvider như là đối số thứ hai đến register() phương pháp.

+0

Xin lỗi vì loại sai. Tôi thay thế tham số thứ hai bằng FooServiceProvider mới() và khiếu nại vẫn tồn tại. –

0

chữ ký Phương pháp của bạn giống như:

public void register(Class clazz, ServiceProvider provider) 

Nhưng bạn đang đi qua hai trường hợp Class để thanh ghi-Phương pháp

registry.register(FooService.class, FooServiceProvider.class); 

Bạn phải vượt qua một thể hiện của một lớp mà thực hiện giao diện ServiceProvider làm đối số thứ hai cho phương thức.

Bạn nên sử dụng các loại chung chung và không có loại thô. ví dụ. Loại đã nhập? thay vì chỉ Lớp

+0

trước tiên hãy đọc các câu trả lời đã có sẵn ... –

+0

sry tôi đã không làm mới trang trước khi thực hiện bài đăng của tôi. Nhưng tôi đưa ra một gợi ý không sử dụng các loại thô và bài viết khác thì không. – Holger

0

Đồ vật lạ, kỳ lạ. Đây là giải pháp của tôi:

public class ServiceProviderRegistry<T extends Service> { 

    private Map<Class<? extends T>, ServiceProvider<? extends T>> map = new HashMap<Class<? extends T>, ServiceProvider<? extends T>>(); 

    public void register(Class<? extends T> clazz, ServiceProvider<? extends T> provider) { 
     map.put(clazz, provider); 
    } 

    public ServiceProvider<? extends T> lookup(Class<? extends T> cls) { 
     return map.get(cls); 
    } 
} 

và chính:

public static void main(String[] args) { 
    ServiceProviderRegistry<Service> registry = new ServiceProviderRegistry<Service>(); 
    registry.register(FooService.class, new FooServiceProvider()); 
    ServiceProvider sp = registry.lookup(FooService.class); 
    Service s = sp.getService(); //safe! 
} 

an toàn và vẫn gõ

+0

Bạn đang sử dụng loại 'ServiceProvider' thô trên dòng cuối cùng của' main'. Nhưng trong thực hiện này 'ServiceProvider sp = registry.lookup (FooService.class); 'sẽ không biên dịch, vì' tra cứu' không bắt buộc 'tra cứu (X.class)' thuộc loại 'ServiceProvider ' (chỉ 'ServiceProvider ') –

+0

Nếu không cast bạn không thể nhận được ServiceProvider từ ServiceProviderRegistry . đây là nhiệm vụ phi logic. Loại cha mẹ không thể được đưa vào con một cách ngầm định. – Mikhail

+0

Đó là quan điểm của tôi - ServiceProviderRegistry không phải là một lớp tham số ở tất cả, nhưng các phương pháp đăng ký và tra cứu phải là chung chung và thực thi việc khớp các loại. –

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