I need to provide TimeStamp
to deserialize
method of JsonDeserializer
in dagger 2.
@Singleton
@Provides
public JsonDeserializer provideJsonDeserializer() {
return new JsonDeserializer() {
public Timestamp deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
return new Timestamp(json.getAsJsonPrimitive().getAsLong());
}
};
}
but that object needs JsonElement
object from JsonDeserializer
. How can i pass that obect to my provideTimestamp
method.
@Singleton
@Provides
public Timestamp provideTimestamp() {
}
Or should I just not inject Timestamp
through dagger, if that's the case can someone explain why, that might help me to learn more about dagger 2.
The way you have it in your first code sample is right, and how I'd have it: You should be calling new Timestamp(...)
, rather than deferring to Dagger.
The distinction you should be drawing is injectables vs newables: Which objects in you graph should be injected, and which objects should be created with new
? It can be tempting to think that every call to new
is bad, because it reduces flexibility, but the reality is that it's more of a judgment call:
See also: To “new” or not to “new”…, by Miško Hevery
As mentioned above, Timestamp is a value object: It is unlikely you'd ever want to substitute the implementation, Timestamp has no dependencies other than per-instance data, and implementations are very easy to create. This makes Timestamp an exceedingly good candidate to be a newable.
In your case, you have an additional wrinkle: Your new timestamp is going to vary based on the json
object you pass in, and the json
object is going to vary enough that it's unlikely that you'd want to put it in your Dagger graph. This means that if you did want to pursue injection, rather than injecting a Timestamp, you would probably want to inject a TimestampFactory, which could be a constructor reference, an AutoFactory-created implementation, or any other implementation:
interface TimestampFactory {
Timestamp create(long value);
}
This seems excessive, but let's pretend instead you want Timestamp2, which also records the current time of day and therefore might make testing difficult. In that case, you could inject your timestamp factory:
@Singleton
@Provides
public JsonDeserializer provideJsonDeserializer(Timestamp2Factory tsFactory) {
return new JsonDeserializer() {
public Timestamp2 deserialize(JsonElement json, Type typeOfT,
JsonDeserializationContext context) throws JsonParseException {
return tsFactory.create(json.getAsJsonPrimitive().getAsLong());
}
};
}
@Reusable
@Provides
public Timestamp2Factory provideTimestamp2Factory() {
// Merge in other parameters and dependencies if needed,
// but Timestamp2 only needs one value that create(long) supplies.
return Timestamp2::new;
// or, pre-Java-8:
return new Timestamp2Factory() {
@Override public Timestamp2 create(long time) {
return new Timestamp2(time);
}
}
}
This would allow you to call provideJsonDeserializer
in your tests and pass in a TimestampFactory of your choosing, such as one that uses a fake system time instead of a real one. This would allow your tests to be much safer and more deterministic.
However, since you want the fast, deterministic value object Timestamp that doesn't have external dependencies like RPC services, file system access, or the actual system time, then keep your new Timestamp
call as in your question.