2012-02-07 8 views
10

Moim celem jest poprawienie wewnątrz kodu scala istniejącej klasy Java za pomocą mieszania cech. Na przykład, aby dodać metodę taką jak java.awt.Rectangle.translate (dx, dy) do klasy java.awt.geom.Ellipse2D. W tym celu utworzyć następującą cechę:Poprawić klasy Java za pomocą cech, jak zadeklarować wewnątrz cech pola java?

trait RectangleLike { 
    var x: Double // abstract vals to correspond to java class fields 
    var y: Double // I need these vars to refer to them inside translate method 
    def translate(dx: Double, dy: Double) { 
    x = x + dx 
    y = y + dy 
    } 
    // more concrete trait methods here 
} // defines without errors in scala REPL 

następnie użyć cechę przy konstruowaniu elipsę:

val egg = new java.awt.geom.Ellipse2D.Double(5, 10, 20, 30) with RectangleLike 

Jednak kiedy wykonać powyższy skrypt w Scala REPL pojawia się następujący komunikat:

<console>:8: error: overriding variable x in trait RectangleLike of type Double; 
variable x in class Double of type Double has incompatible type; 
other members with override errors are: y 
val egg = new java.awt.geom.Ellipse2D.Double(5, 10, 20, 30) with RectangleLike 

Podejrzewam, że ten błąd wynika ze sposobu, w jaki Scala implementuje vars - jako pole prywatne i parę metod getter/setter. Czy to, co staram się osiągnąć? Czy istnieje inny sposób definiowania pól klasy java w tej cechie, a następnie odwoływanie się do nich w metodach konkretnych cech?

góry dzięki Jack Dimas

Odpowiedz

8

Tak, to jest wykonalne, ale zamiast próbować uzyskać dostęp do prywatnych pól klas chcesz mieszać się z (co jest najbardziej prawdopodobne jest to zły pomysł i tak), byś chcesz zadeklarować własny typ RectangleLike, aby być java.awt.geom.RectangularShape, abyś mógł użyć swojej cechy z np. Ellipse2D.Double równie dobrze jak z Rectangle2D.Double.

Oto jak to działa:

trait RectangleLike { 
    self: java.awt.geom.RectangularShape => 

    def translate(dx: Double, dy: Double) { 
    setFrame(getX + dx, getY + dy, getX + getWidth, getY + getHeight) 
    } 
} 

object Test { 
    val foo = new java.awt.geom.Ellipse2D.Double with RectangleLike 
} 

Mówiąc self: java.awt.geom.RectangularShape => zadeklarować własny typ swojej cecha, która pozwala na dostęp do wszystkich odpowiednich metod, takich jak niezbędne pobierające i ustawiające, pozwala na korzystanie z cechę z wszyscy potomkowie RectangularShape, a także "ograniczają" twoją cechę, aby mogła być używana tylko jako mixin do klas, które same są podtypami RectangularShape.

alternatywa do powyższej sytuacji jest użycie tzw widok swojego RectangularShape który jest wspólny paradygmat, jak również. W tym celu możesz np. zadeklarować klasę

class RichRectangularShape(shape: java.awt.geom.RectangularShape) { 
    def translate(dx: Double, dy: Double) { 
    shape.setFrame(shape.getX + dx, shape.getY + dy, 
        shape.getX + shape.getWidth, shape.getY + shape.getHeight) 
    } 
} 

Scala ma sposób dorozumiany oglądania przedmiotem jednego typu jako obiekt innego typu. Jeśli zdarzy ci się wywołać metodę na obiekcie, który nie jest zadeklarowany w odpowiadającym mu typie, kompilator próbuje znaleźć typ, który udostępnia tę metodę, aw szczególności próbuje znaleźć niejawną konwersję, aby oryginalny obiekt mógł być postrzegany jako obiekt przykład tego ostatniego typu. Aby to działało, to zazwyczaj zadeklarować obiekt towarzysz RichRectangularShape jak coś takiego:

object RichRectangularShape { 
    implicit def mkRRS(shape: java.awt.geom.RectangularShape): RichRectangularShape = 
    new RichRectangularShape(shape) 
} 

Następnie:

scala> import RichRectangularShape._ 
import RichRectangularShape._ 

scala> val foo = new java.awt.geom.Ellipse2D.Double 
foo: java.awt.geom.Ellipse2D.Double = [email protected] 

scala> foo.translate(5,2) 

scala> foo.getX 
res1: Double = 5.0 

scala> foo.getY 
res2: Double = 2.0 

scala> :t foo 
java.awt.geom.Ellipse2D.Double 
+1

@forNelton imponujący! Dzięki mil naprawdę się na tym utknąłem. Muszę badać ukryte poglądy. – ideathbird

+0

Nie ma za co. W zależności od rodzaju aplikacji, myślę, że pierwsze podejście może być bardziej odpowiednie, ponieważ wywołania metod mogą być znacznie szybsze bez wszystkich ukrytych problemów. – fotNelton

0

Rzeczywiście imponujące wyjaśnienia i przykład!

Mam jeden mały problem z tym podejściem, metoda translate zawiera rzeczywiste obliczenia, których chciałbym uniknąć. Tak, w tym przypadku jest to bardzo proste, ale ogólnie rzecz biorąc taka metoda może być skomplikowana i prowadzić do poważnego rozwoju.Proponuję zatem następujące podejście:

trait RectangleLike extends java.awt.geom.RectangularShape { 
    def translate(dx: Int, dy: Int): Unit = { 
     val rect = new java.awt.Rectangle 
     rect.setRect(getX, getY, getWidth, getHeight) 
     rect.translate(dx,dy) 
     setFrame(rect.getX, rect.getY, rect.getWidth, rect.getHeight) 
     } 
} 

w ten sposób używam oryginalnego obliczenia translate.