Reputation: 3482
In Scala code, I have
val pendingServices = new ConcurrentHashMap[String, JMap[Class[_ <: Service], Service]]()
From the caller side, Java, using looks like
Service$.MODULE$.pendingServices().get("key").put(Foo.class, new Foo());
Also I am able to get instance of some Service
class by
Service$.MODULE$.pendingServices().get("key").get(Foo.class)
or from Scala,
Service.pendingServices.get("key").get(classOf[Foo])
The problem: it is possible to put unrelated Service
es in the map, like
Service$.MODULE$.pendingServices().get("key").put(Bar.class, new Foo());
if Foo
and Bar
both extends Service
. So, how can I restrict Class[_ <: Service
and Service
from the definition to share same Service
?
I want to demonstrate my coworkers some cases when Scala's type system really helps. Tried with type lambdas, but my type-fu is not strong enought.
Upvotes: 4
Views: 144
Reputation: 16308
You could use such construction in the scala code
import scala.collection.mutable.{HashMap ⇒ MHMap, SynchronizedMap}
import scala.reflect.{ClassTag, classTag}
class Service
case class Foo() extends Service
case class Bar() extends Service
object Service {
object pendingServices {
private val backing = (new MHMap[String, Map[Class[_], Service]] with SynchronizedMap)
.withDefaultValue(Map.empty)
def update[T <: Service : ClassTag](name: String, service: T): Unit =
backing(name) += classTag[T].runtimeClass → service
def apply[T <: Service : ClassTag](name: String): T =
backing(name)(classTag[T].runtimeClass).asInstanceOf[T]
}
}
in scala you could use it as
val foo = new Foo
val bar = new Bar
pendingServices("baz") = foo
pendingServices("baz") = bar
println(pendingServices[Foo]("baz")) // Foo()
println(pendingServices[Bar]("baz")) // Bar()
in java equivalent would be
final Foo foo = new Foo();
final Bar bar = new Bar();
Service.pendingServices$.MODULE$.update("baz", foo, ClassTag$.MODULE$.apply(Foo.class));
Service.pendingServices$.MODULE$.update("baz", bar, ClassTag$.MODULE$.apply(Bar.class));
System.out.println(Service.pendingServices$.MODULE$.<Foo>apply("baz", ClassTag$.MODULE$.apply(Foo.class))); // Foo()
System.out.println(Service.pendingServices$.MODULE$.<Bar>apply("baz", ClassTag$.MODULE$.apply(Bar.class))); // Bar()
It still does not restrict anything outside scala, but can work as demo
Upvotes: 3
Reputation: 12440
I think you'll have to wrap the map into some helper class. You could use something like (Java syntax):
public class ServiceRepository {
private Map<Class<?>, Object> repo = new HashMap<>();
public <T> void put(Class<T> clazz, T service) {
repo.put(clazz, service);
}
public <T> T get(Class<T> clazz) {
return (T) repo.get(clazz);
}
}
Upvotes: 2