2012-03-03 81 views
9

工厂方法添加到现有的Java类在纯Scala的环境中,我可以做到以下几点,如果我想一个工厂方法“添加”到现有对象:如何在斯卡拉

object Test 

object Extensions { 

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

} 

... 

import Extensions._ 
val t = Test.someFactory 

我需要将这样的功能与现有的Java类结合使用。在我的具体例子中,我想添加一个工厂方法fromLocation到类com.google.android.maps.GeoPoint(我想每个Android开发人员都会知道为什么这会很有用;-))。

但是,如果我尝试做一些像

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

我得到一个错误,说明

类型不匹配;发现:com.google.android.maps.GeoPoint.type(与基础类型的对象com.google.android.maps.GeoPoint)要求:AnyRef

所以我不知道是否有任何方式上面的方法怎么可能被实现 - 或者将提供从LocationGeoPoint的隐式转换是Scala的首选方式,因此只要需要GeoPoint就可以使用Location


的要求,在评论中,使用场景:

// 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, ...) 
    .... 
} 

但是,请记住,这仅仅是一个

+0

你能否举一个使用这个工厂方法的代码的例子吗?例如,使用GeoPoints和Locations。我不太了解这个问题。 – Fixpoint 2012-03-05 23:19:18

+0

@完成点;-) – Leo 2012-03-06 11:05:58

回答

2

由于斯卡拉版本2.9.1这是不可能的用工厂方法扩展Java类。

然而,有三种不同的解决方案:

  1. Scala compiler plugin以适应所需的更改,然后扩展Java类。
  2. 创建一个独立的工厂方法。
  3. 避免完全使用工厂方法并添加隐式转换,以便始终可以使用Location对象来代替GeoPoint。

个人而言,我会去的第三个选择,如果我用Location - >GeoPoint或任何其它转换了很多的时间,特别是在场景转换时,可始终没有异常和类接口做不交叠。

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, ...) 
} 
0

你想要什么普遍的问题;-)具体的例子要做是不可能的。由于Java类不是Scala对象,因此无法使用方法来放大它。

的唯一一点我看,使工作尽可能容易是建立在Scala中的对象,使合格的进口为Java类:

import test.{ Test => JTest } 
object Test { 
    def anyMethodHere ... 
} 
+0

......这绝对不是我想要的。 Java中的工厂方法是一种静态方法;斯卡拉等价物是对象方法,对吧?我可以在Scala中调用静态Java方法,所以必须有某种静态到对象的转换。最后,我想通过Scala将一个静态方法添加到Java类中。这意味着新关键字是我不想要的东西。 – Leo 2012-03-03 16:08:12

+0

@Mef:对不起,我错过了这一点。尽管我无法真正帮助你,但我更新了答案。 – sschaef 2012-03-03 17:20:54

2

大问题!不幸的是,我不认为这是可能的。由于在Java中没有办法将类的静态部分用作值,所以Java类的静态成员的类型没有理由扩展AnyRef。不幸的是,为了使Java对象扩展AnyRef,你需要使用隐式转换,这需要Java对象来扩展AnyRef ...

虽然我很想被证明是错误的!

更新:你不能在Java中做到这一点,我认为最好的做法是创建你自己的静态类,在其中添加工厂方法。例如,请考虑Guava中的List

更新:这是Java和Scala(Dario描述的)之间差异的完整示例。

# vim Test.java 
class Test { 
    public static final int foo = 1; 
    public final int bar = 2; 
} 

# javac Test.java 
# ls 

Test.class Test.java 

# javap Test 

Compiled from "Test.java" 
class Test { 
    public static final int foo; 
    public final int bar; 
    Test(); 
} 

相比,使用Scala:

# vim Test.scala 

object Test { 
    val foo = 1 
} 

class Test { 
    val bar = 2 
} 

# javac Test.scala 
# ls 

Test$.class Test.class Test.scala 

# javap Test 

public class Test implements scala.ScalaObject { 
    public static final int foo(); 
    public int bar(); 
    public Test(); 
} 

# javap Test$ 

Compiled from "Test.scala" 
public final class Test$ implements scala.ScalaObject { 
    public static final Test$ MODULE$; 
    public static {}; 
    public int foo(); 
} 
2

这是不可能的,因为在object ...阶将成为单一实例通过Java类的静态方法进行访问。例如。

object Foo { 
    def bar = 2 
} 

将成为类似:

public final class Foo { 
    public static int bar() { 
    return Foo..MODULE$.bar(); 
    } 
} 

public final class Foo$ 
    implements ScalaObject 
{ 
    public static final MODULE$; 

    static 
    { 
    new(); 
    } 

    public int bar() 
    { 
    return 2; 
    } 

    private Foo$() 
    { 
    MODULE$ = this; 
    } 
} 

什么被传递到隐式转换的方法是在这种情况下,Foo..MODULE $这是Foo类型$的一个实例。 Java静态方法没有底层的单例实例,因此不能被传递到要转换为另一种类型的函数中。

希望这有助于理解为什么它是不可能的;-)。

1

虽然您不能通过隐式转换来添加一种方法到这种单例对象,但您可以使用隐式参数来进行实际的创建。例如:

trait Factory1[-A,+B] { 
    def buildFrom(a: A): B 
} 

trait Factory2[-A1,-A2,+B] { 
    def buildFrom(a1: A1, a2: A2): B 
} 

object Factories { 
    implicit object GeoPointFromLocation extends Factory1[Location,GeoPoint] { 
    def buildFrom(location: Location): GeoPoint = //actual code 
    } 
    implicit object GeoPointFromXY extends Factory2[Double,Double,GeoPoint] { 
    def buildFrom(x: Double, y: Double): GeoPoint = //actual code 
    } 
} 

object GeoPoint { 
    def from[A](a: A)(implicit fac: Factory1[A,GeoPoint]) = fac.buildFrom(a) 
    def from[A,B](a: A, b: B)(implicit fac: Factory2[A,B,GeoPoint]) = fac.buildFrom(a,b) 
} 

import Factories._ 
val loc = new Location(...) 
val gp1 = GeoPoint.from(loc) 
val gp2 = GeoPoint.from(101.0, -12.6) 

所以工厂仍然是“静态”你似乎期望他们,但你可以丰富或改变行为,而不必改变GeoPoint对象。例如,您可以在测试时导入特殊工厂。

此方法用于Scala库中,例如,在应用通用Traversable方法(如map)时构建正确的集合类型。

+0

...导致一个'导入的GeoPoint被封装中的对象GeoPoint的定义永久隐藏......'所以我猜这在实际中并不真正解决:-( – Leo 2012-03-12 11:44:03

0

这是不可能的,但你可以有相同的类名同伴对象包对象,并把它作为你想,

import com.google.android.maps.GeoPoint 

package object com.xyz.mappy.maps { 
    object GeoPoint {   
    def from(....): GeoPoint = new GeoPoint(...) 
    } 
} 


................................. 

package com.xyz.mappy.maps 

import com.google.android.maps.GeoPoint 

class MapRenderer { 

    val geopoint = GeoPoint.from(...) 

}