2012-03-03 78 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(...) 

}