1

简短回答:绑定到对象实例的 bind() 方法应提供 Class<Object> 参数。那说:

Class<?> type = got_a_type(); Object object = got_an_object();
// Illegal - compilation error because of type check comparing ? to Object
bind(type).toInstance(object);
// Legal and working 
bind((Class<Object>)type).toInstance(object);

很长的故事:

我有来自旧系统的 json 配置文件,格式如下:

{
    "$type": "test_config.DummyParams",
    "$object": {
        "stringParam": "This is a string",
        "integerParam": 1234,
        "booleanParam": false
    }
}

test_config.DummyParams 是程序运行时可用的类,如下所示:

package test_config;

public class DummyParams {
    public String stringParam;
    public int integerParam;
    public boolean booleanParam;
}

我想通过 guice 创建一些类,它具有 DummyParams 类型的构造函数参数(需要注入):

@Inject
public class DummyService(DummyParams params) { ... }

现在,由于 DummyParams 类仅在运行时提供(通过 json 配置文件)并且在编译时无法知道我不能在 guice 绑定中使用这种类型:

// Can't do this because DummyParams type should come from config file
Object object = ...; // Getting object somehow
bind(DummyParams.class).toInstance((DummyParams)object);

我有一些旧代码,它为我提供了从所有 json 配置文件中读取的类和对象(类型和实例)对:

class ConfigObject { 
    Class<?> type;
    Object instance;
}

我试图简单地绑定它们:

ConfigObject obj = config.read(); // Getting pairs from config files walker
bind(obj.type).toInstance(obj.instance);

但这是不可编译的:“com.google.inject.binder.LinkedBindingBuilder 中的 java:toInstance(capture#189 of ?) 不能应用于 (java.lang.Object)”。

所以这里有一个问题:如何绑定在运行时确定的类型的实例?我是否打破了 IoC 的概念并且应该做我想做的事情?

4

2 回答 2

3

我认为您可以在这里安全地使用类型转换。这是一个完整的例子:

public class Main {
    public static void main(String[] args) {
        final Holder holder = new Holder("abcd", String.class);

        Injector injector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bind((Class<Object>) holder.type).toInstance(holder.instance);
            }
        });

        System.out.println(injector.getInstance(String.class));  // Prints "abcd"
    }

    public static class Holder {
        public final Class<?> type;
        public final Object instance;

        public Holder(Object instance, Class<?> type) {
            this.instance = instance;
            this.type = type;
        }
    }
}

或者您可以将问题移到上层并将您的配置对象更改为包含Class<Object>;那么你将不得不在你的配置阅读器中强制转换类:

public class Main {
    public static void main(String[] args) {
        final Holder holder = new Holder("abcd", (Class<Object>) (Class<?>) String.class);

        Injector injector = Guice.createInjector(new AbstractModule() {
            @Override
            protected void configure() {
                bind(holder.type).toInstance(holder.instance);
            }
        });

        System.out.println(injector.getInstance(String.class));
    }

    public static class Holder {
        public final Class<Object> type;
        public final Object instance;

        public Holder(Object instance, Class<Object> type) {
            this.instance = instance;
            this.type = type;
        }
    }
}

不过,这将需要通过通配符类型或类似的东西进行转换。

然而,整个情况真的很奇怪。如果您的配置类没有通用的超类型,它们将如何使用?如果您事先不知道您的对象属于哪个类,那么您不能(几乎)在没有反射或instanceofs 的情况下使用它。

于 2013-07-02T07:56:21.353 回答
1

使用和滥用原始类型、隐式未经检查的强制转换和一些番石榴库:

public class RuntimeParams {
  public static void main(String[] args) throws ClassNotFoundException, IOException {

    // reads from config and bind in runtime
    final ConfigReader config = new ConfigReader();
    final ConfigObject configObject = config.read();
    final Injector injector = Guice.createInjector(new AbstractModule() {
      @Override protected void configure() {
        bind(configObject.type).toInstance(configObject.instance);
      }
    });

    // use the binded instance
    final DummyParams instance = injector.getInstance(DummyParams.class);
    System.out.println("DummyParams' stringParam: " + instance.stringParam + "\nDummyParams' integerParam: "
        + instance.integerParam + "\nDummyParams' booleanParam: " + instance.booleanParam);
  }
}

class ConfigObject<T> {
  private static final Gson gson = new Gson();

  final T instance;
  final Class<T> type;

  ConfigObject(final String json, final Class<T> type) {
    this.instance = gson.fromJson(json, type);
    this.type = type;
  }
}

class ConfigReader {
  private static final Gson gson = new Gson();

  ConfigObject read() throws ClassNotFoundException, IOException {
    try (final FileReader reader = new FileReader(new File("src/main/resources/runtimeClazzNParams.json"));) {
      final Map<String, Object> configMap = gson.fromJson(reader, Types.mapOf(String.class, Object.class));
      final Class<?> clazz = Class.forName((String) configMap.get("$type"));
      return new ConfigObject<>(gson.toJson(configMap.get("$object")), clazz);
    }
  }
}

它类似于 Vladimir 的解决方案,但使用强制转换隐式和原始类型,而不是直接 Object 引用。

我还使用 Gson guava 类方法编写了一种解析 JSON 的新方法。

于 2013-07-02T10:45:41.537 回答