8

Javaジェネリックについて「奇妙な」問題があります。

まず、コードをリストします。

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());
     }

}

ServiceTest クラスで、コンパイラは、registry.register メソッドが渡された引数に適用できないと訴えます。なぜこれが起こるのか、私には本当にわかりません。ですから、この問題を解決するためにあなたの助けを期待しています。

4

4 に答える 4

6

この場合、パラメータ化されたクラスでServiceProviderRegistryなく、代わりにregisterメソッド (およびおそらく対応するルックアップ メソッド) をジェネリックにする方がよいでしょう。現在のアプローチでは、 Service のサブクラスではなく登録のみServiceProviderRegistry<Service>が可能です。Service.class

本当に重要なのは、渡されたクラスとプロバイダーがregister互いに一致するようにすることだけです。これは、ジェネリック メソッドの理想的なケースです。

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);
  }
}

注釈が必要になり@SuppressWarningsます。コンパイル時の型にしかアクセスできないコンパイラを完全に満足させる方法で、注釈なしでこのパターンを実装することは不可能です。この場合、マップregisterを変更する唯一のものであるため、キャストは実行時に常に安全であることがわかっているため、正当化されます。 この関連する質問に対するJon Skeetの答えは、それを非常にうまくまとめていますregistry@SuppressWarnings

Javaジェネリックでは、やりたいことを実行できない場合があり、実行時に実際に行っていることが合法であることをコンパイラーに効果的に伝える必要があります。

于 2013-06-07T08:22:05.937 に答える
5

メソッドシグネチャは

 public void register(Class clazz, ServiceProvider provider)

Classここで2つのインスタンスを渡しています:

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

ServiceProviderメソッドの 2 番目の引数として、インターフェイスを実装するクラスのインスタンスを渡す必要がありますregister()

于 2013-06-07T08:02:29.167 に答える