javascalaimplicits

How to add a factory method to an existing Java class in Scala


In a pure Scala environment, I could do the following if I wanted to "add" a factory method to an existing object:

object Test

object Extensions {

    object RichTest {
        def someFactory = new Test()
    }
    implicit def fromTest(t: Test.type) = RichTest

}

...

import Extensions._
val t = Test.someFactory

I would need such a functionality in combination with an existing Java class. In my concrete example, I would like to add a factory method fromLocation to the class com.google.android.maps.GeoPoint (and I guess every Android developer will know why this would be useful ;-) ).

However, if I try to do something like

implicit def fromGeoPoint(t: GeoPoint.type) = RichTest

I get an error stating

type mismatch; found : com.google.android.maps.GeoPoint.type (with underlying type object com.google.android.maps.GeoPoint) required: AnyRef

So I wonder if there is any way how the above approach could be implemented - or would providing an implicit conversion from Location to GeoPoint be the preferred way in Scala so a Location could be used whenever a GeoPoint is required?


As requested in the comments, a usage scenario:

// Callback you get from the GPS
override def onLocationChanged(l: Location) {
    // You want to put a marker on a map, hence a GeoPoint is required
    val gp: GeoPoint = GeoPoint.fromLocation(location)
    val item = new OverlayItem(gp, ...)
    ....
}

However, keep in mind that this is just one specific example for the general problem ;-)


Solution

  • As of Scala version 2.9.1 it is not possible to extend Java class with a factory method.

    However, there are three alternative solutions:

    1. Write Scala compiler plugin to accommodate the changes required and then extend the Java class.
    2. Create a standalone factory method.
    3. Avoid using a factory method altogether and add an implicit conversion so that Location object can always be used in place of a GeoPoint.

    Personally I'd go for the third option if I used Location -> GeoPoint or any other conversion a lot of the time, especially in the scenario when conversion can always be done without exceptions and class interfaces don't overlap.

    implicit def geoPointFromLocation(l: Location):GeoPoint = new GeoPoint...
    
    override def onLocationChanged(l: Location) {        
        val gp: GeoPoint = l  // let Scala compiler do the conversion for you
        val item = new OverlayItem(gp, ...)
        ....
        // or just pass the location to OverlayItem constructor directly
        val item2 = new OverlayItem(l, ...)
    }