2012-02-07 37 views
10

Mục tiêu của tôi là tăng cường bên trong mã scala một lớp Java hiện có bằng cách sử dụng một kết hợp đặc điểm. Ví dụ để thêm một phương thức như java.awt.Rectangle.translate (dx, dy) vào lớp java.awt.geom.Ellipse2D. Đối với điều này tôi tạo ra các đặc điểm sau:Tăng cường các lớp học java bằng cách sử dụng các đặc điểm, cách khai báo bên trong các đặc điểm của các trường 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 

Sau đó sử dụng các đặc điểm khi xây dựng một Ellipse:

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

Tuy nhiên khi tôi thực thi kịch bản nêu trên trong scala REPL tôi nhận được kết quả như sau:

<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 

Tôi nghi ngờ rằng lỗi này là do cách Scala thực hiện các vars - như một trường riêng và một cặp getter/setter của các phương thức. Là những gì tôi cố gắng để đạt được doable? Có cách nào khác để xác định các lĩnh vực lớp java trong đặc điểm và sau đó tham khảo chúng bên trong các phương pháp đặc điểm cụ thể?

Cảm ơn trước Jack Dimas

Trả lời

8

Vâng, nó là doable nhưng thay vì cố gắng truy cập vào các lĩnh vực riêng của lớp bạn muốn kết hợp với (mà rất có thể là một ý tưởng tồi anyway), bạn sẽ muốn khai báo kiểu tự của RectangleLike thành java.awt.geom.RectangularShape để bạn có thể sử dụng đặc điểm của mình với ví dụ Ellipse2D.Double cũng như với Rectangle2D.Double.

Dưới đây là cách hoạt động:

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 
} 

Bằng cách nói self: java.awt.geom.RectangularShape => bạn khai báo tự loại đặc điểm của bạn cho phép bạn truy cập tất cả các phương pháp tương ứng như getter và setter cần thiết, cho phép sử dụng đặc điểm của bạn với tất cả các hậu duệ của RectangularShape và cũng "hạn chế" đặc điểm của bạn để nó chỉ có thể được sử dụng làm bản mixin cho các lớp mà chính chúng là các kiểu con của RectangularShape.

Các thay thế với kịch bản trên được sử dụng một cái gọi là xem của RectangularShape của bạn mà là một mô hình chung là tốt. Đối với điều này, bạn sẽ ví dụ: khai báo một lớp

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 có một cách để ngầm xem một đối tượng của một loại như một đối tượng của loại khác. Nếu bạn tình cờ gọi một phương thức trên một đối tượng không được khai báo trong kiểu tương ứng của nó, trình biên dịch sẽ tìm ra một kiểu cung cấp phương thức này và đặc biệt là để tìm một chuyển đổi ngầm để đối tượng ban đầu của bạn có thể được xem như là một ví dụ của loại sau. Để làm việc này, bạn sẽ thường khai báo đối tượng đồng hành của RichRectangularShape như một cái gì đó như thế này:

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

Sau đó:

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 ấn tượng! Cảm ơn một triệu Tôi thực sự đã bị mắc kẹt về điều này. Tôi phải nghiên cứu các quan điểm ngầm định. – ideathbird

+0

Bạn được chào đón. Tùy thuộc vào loại ứng dụng của bạn, tôi nghĩ cách tiếp cận đầu tiên có thể phù hợp hơn vì các cuộc gọi phương thức có khả năng nhanh hơn mà không có tất cả các rắc rối tiềm ẩn. – fotNelton

0

giải Quả thật ấn tượng và gương!

Tôi có một vấn đề nhỏ với cách tiếp cận này, phương pháp translate bao gồm tính toán thực sự mà tôi muốn tránh. Có trong trường hợp này nó rất đơn giản nhưng nói chung phương pháp như vậy có thể phức tạp và dẫn đến sự phát triển nghiêm trọng.Do đó, tôi đề xuất cách tiếp cận sau:

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) 
     } 
} 

Bằng cách này, tôi đang sử dụng phép tính translate ban đầu.

Các vấn đề liên quan