开发者

Spring: Injecting a Scala List

This can be done using a converter wrapper:

import scala.collection.JavaConversions._

object ListConverter {

  def toScalaList( jlist: java.util.List[AnyRef] ) = {
    jlist.toList
  }
}

which uses a implicit def asScalaBuffer in JavaConversions to return a Scala List. Then I can make the list injectable as:

<bean id="someItems" class="my.package.ListConverter" factory-method="toScalaList">
    <constructor-arg type="java.util.List">
        <util:list>
            开发者_开发知识库<ref bean="itemOne"/>
            <ref bean="itemTwo"/>
        </util:list>
    </constructor-arg>
</bean>

I would like to find a cleaner way to do this

I can't really use JavaConversions directly as a static factory call:

<bean id="someItems" class="scala.collection.JavaConversions" factory-method="asScalaBuffer">...</bean>

Since an implicit def is not really a static method in a Java universe.

P.S. To get it out of the way.. I know there are a lot of annotation driven config lovers, but I am not one of them, and that is not a solution I am looking for: I like my XML for Spring configs, and I like Spring to DI Scala


If you want to convert a java.util.List to a scala List you will have to provide your own conversion/factory method as no such method exists.

JavaConversions.asScalaBuffer could be referenced exactly as you did in your XML definition as the Scala compiler generates static forwarder methods to the object instance unless you tell it not to do so. The problem here is actually actually that this method returns a scala.collection.mutable.Buffer which you still have to convert into a List.

An alternative is to accept an instance of Seq[A] to be injected. This would work as Buffer[A] extends Seq[A] - but you have to be aware that you are injecting a wrapper around the (possibly mutable) java.util.List<A> instance then.


If your ListConverter was an object rather than a class, would not toScalaList be accepted as a static method ?


Your biggest problem is that, behind the scenes, Spring is inherently untyped. That list you're attempting to inject actually starts out as a List<String> before Spring determines the correct type of the target param/property.

In some cases, it can do this because the relevant type is retained in bytecode (even if it's erased on the JVM), this won't work in the case of injecting to generic methods. In other cases, it can determine the type of a collection by inspecting elements. It usually gets this right, depending on how much polymorphism is involved.

Of course, all that logic is based entirely on java.util.Collection and subclasses, so Scala's collection types simply don't get to join the party.

You have three solutions here:

  1. Learn to love annotation-driven injection. You won't be bitten by the insidious weak-typing.

  2. Create an overload of the setter method, or an auxiliary constructor that takes equivalent Java collection types. Then use collection.JavaConverters to handle the conversion. Spring is then happy because it's still within its Java-only comfort zone.

  3. (my personal favourite) Stop using Spring

You're guaranteed to eventually run into problems with your converter, this will happen at around the time you discover the need to deal with the parity mismatch between boxed and unboxed primitives.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜