Đóng gói các đối tượng


92

Đối tượng gói là gì, không phải là quá nhiều khái niệm mà là cách sử dụng của chúng?

Tôi đã cố gắng lấy một ví dụ hoạt động và biểu mẫu duy nhất tôi phải làm việc như sau:

package object investigations {
    val PackageObjectVal = "A package object val"
}

package investigations {

    object PackageObjectTest {
        def main(args: Array[String]) {
            println("Referencing a package object val: " + PackageObjectVal)
        }
    }
}

Các quan sát mà tôi đã thực hiện cho đến nay là:

package object _root_ { ... }

không được phép (điều đó là hợp lý),

package object x.y { ... }

cũng không được phép.

Có vẻ như một đối tượng gói phải được khai báo trong gói mẹ ngay lập tức và nếu được viết như trên, biểu mẫu khai báo gói được phân tách bằng dấu ngoặc nhọn là bắt buộc.

Chúng có được sử dụng phổ biến không? Nếu vậy, làm thế nào?



1
@Brent, đây là một tài nguyên tuyệt vời, không chỉ dành cho bài viết về đối tượng gói. Tôi đã nghe nói về tác giả nhưng không nhận ra anh ấy đã viết chuyến tham quan Scala này, cảm ơn.
Don Mackenzie

Câu trả lời:


128

Thông thường, bạn sẽ đặt đối tượng gói của mình trong một tệp riêng được gọi package.scalatrong gói tương ứng với nó. Bạn cũng có thể sử dụng cú pháp gói lồng nhau nhưng điều đó khá bất thường.

Trường hợp sử dụng chính cho các đối tượng gói là khi bạn cần định nghĩa ở nhiều nơi khác nhau bên trong gói của bạn cũng như bên ngoài gói khi bạn sử dụng API do gói định nghĩa. Đây là một ví dụ:

// file: foo/bar/package.scala

package foo

package object bar {

  // package wide constants:
  def BarVersionString = "1.0"

  // or type aliases
  type StringMap[+T] = Map[String,T]

  // can be used to emulate a package wide import
  // especially useful when wrapping a Java API
  type DateTime = org.joda.time.DateTime

  type JList[T] = java.util.List[T]

  // Define implicits needed to effectively use your API:
  implicit def a2b(a: A): B = // ...

}

Bây giờ các định nghĩa bên trong đối tượng gói đó có sẵn bên trong toàn bộ gói foo.bar. Hơn nữa, các định nghĩa được nhập khi ai đó bên ngoài gói đó nhập foo.bar._.

Bằng cách này, bạn có thể ngăn chặn việc yêu cầu ứng dụng khách API phát hành các lần nhập bổ sung để sử dụng thư viện của bạn một cách hiệu quả - ví dụ: trong scala-swing, bạn cần viết

import swing._
import Swing._

để có tất cả sự tốt đẹp như onEDTvà chuyển đổi ngầm từ Tuple2sang Dimension.


13
Lưu ý: tính năng nạp chồng phương thức không hoạt động trong các đối tượng gói.
viết tắt

Hãy cho tôi biết lý do tại sao nó được chọn rằng đối tượng gói phải được xác định một cấp trong hệ thống phân cấp gói. Ví dụ: điều này có nghĩa là bạn cần phải gây ô nhiễm gói ảo orghoặc comgói cấp cao nhất với đối tượng gói của bạn nếu bạn muốn nó thuộc về gói gốc của riêng bạn, ví dụ org.foo. Tôi thấy rằng việc cho phép định nghĩa nằm ngay dưới gói nó nên là một phần của nó - lẽ ra sẽ là giao diện api ngôn ngữ thích hợp hơn một chút.
matanster

58

Trong khi câu trả lời của Moritz là đúng, một điều bổ sung cần lưu ý là các đối tượng gói là các đối tượng. Trong số những thứ khác, điều này có nghĩa là bạn có thể xây dựng chúng từ các đặc điểm, bằng cách sử dụng kế thừa kết hợp. Ví dụ của Moritz có thể được viết như

package object bar extends Versioning 
                          with JodaAliases 
                          with JavaAliases {

  // package wide constants:
  override val version = "1.0"

  // or type aliases
  type StringMap[+T] = Map[String,T]

  // Define implicits needed to effectively use your API:
  implicit def a2b(a: A): B = // ...

}

Ở đây Phiên bản là một đặc điểm trừu tượng, nói rằng đối tượng gói phải có phương thức "phiên bản", trong khi JodaAliases và JavaAliases là các đặc điểm cụ thể chứa bí danh kiểu tiện dụng. Tất cả các đặc điểm này có thể được sử dụng lại bởi nhiều đối tượng gói khác nhau.


Toàn bộ chủ đề đang mở ra rất nhiều và nó dường như đã được sử dụng hết tiềm năng của nó, cảm ơn một ví dụ phong phú khác.
Don Mackenzie

1
nhưng chúng không thể được sử dụng như vals, vì vậy chúng không thực sự là đồ vật
Eduardo Pareja Tobes

7

@Alex Cruise, cảm ơn, điều này có vẻ gợi ý rằng họ cần một đơn vị biên dịch riêng biệt (có lẽ sẽ giải quyết được hạn chế gói được phân tách bằng dấu ngoặc kép). Vấn đề là tôi muốn một số lời khuyên người dùng vững chắc hơn là phỏng đoán của riêng tôi về cách sử dụng chúng.
Don Mackenzie

5

Trường hợp sử dụng chính cho các đối tượng gói là khi bạn cần định nghĩa ở nhiều nơi khác nhau bên trong gói của bạn cũng như bên ngoài gói khi bạn sử dụng API do gói định nghĩa.

Không như vậy với Scala 3 , dự kiến ​​phát hành vào giữa năm 2020, dựa trên Dotty , như ở đây :

Định nghĩa Toplevel

Tất cả các loại định nghĩa có thể được viết trên toplevel.
Các đối tượng gói không còn cần thiết, sẽ bị loại bỏ dần.

package p 

type Labelled[T] = (String, T) 
val a: Labelled[Int] = ("count", 1) 
def b = a._2 
def hello(name: String) = println(i"hello, $name)

Cảm ơn @VonC, tôi thực sự mong đợi Scala 3 vì điều này và nhiều lý do khác. Tôi đã không sử dụng nhiều các đối tượng gói nhưng tôi chắc chắn rằng tôi sẽ sử dụng các định nghĩa cấp cao nhất.
Don Mackenzie
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.