Mengemas objek

92

Apa objek paket, bukan begitu banyak konsepnya tetapi penggunaannya?

Saya sudah mencoba untuk mendapatkan contoh bekerja dan satu-satunya bentuk saya harus bekerja adalah sebagai berikut:

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

Pengamatan yang saya lakukan sejauh ini adalah:

package object _root_ { ... }

tidak diizinkan (yang masuk akal),

package object x.y { ... }

juga tidak diizinkan.

Tampaknya objek paket harus dideklarasikan dalam paket induk langsung dan, jika ditulis seperti di atas, diperlukan formulir deklarasi paket yang dipisahkan tanda kurung kurawal.

Apakah mereka umum digunakan? Jika ya, bagaimana caranya?

Don Mackenzie
sumber
1
@Brent, ini adalah sumber daya yang bagus, bukan hanya untuk artikel objek paket. Saya pernah mendengar tentang penulisnya tetapi tidak menyadari bahwa dia telah menulis tur Scala ini, terima kasih.
Don Mackenzie

Jawaban:

128

Biasanya Anda akan meletakkan objek paket Anda dalam file terpisah yang disebut package.scaladalam paket yang sesuai dengannya. Anda juga dapat menggunakan sintaks paket bersarang tetapi itu sangat tidak biasa.

Kasus penggunaan utama objek paket adalah saat Anda membutuhkan definisi di berbagai tempat di dalam paket Anda serta di luar paket saat Anda menggunakan API yang ditentukan oleh paket. Berikut ini contohnya:

// 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 = // ...

}

Sekarang definisi di dalam objek paket itu tersedia di dalam keseluruhan paket foo.bar. Selanjutnya definisi diimpor ketika seseorang di luar impor paket itu foo.bar._.

Dengan cara ini Anda dapat mencegah agar klien API mengeluarkan impor tambahan untuk menggunakan perpustakaan Anda secara efektif - misalnya dalam scala-swing Anda perlu menulis

import swing._
import Swing._

untuk memiliki semua kebaikan onEDTdan konversi implisit dari Tuple2menjadi Dimension.

Moritz
sumber
13
Perhatian: overloading metode tidak bekerja di objek paket.
retronim
Beats me why telah dipilih bahwa objek paket harus didefinisikan satu tingkat di atas hierarki paket. Misalnya, ini berarti Anda perlu mencemari paket virtual orgatau comtingkat atas dengan objek paket Anda jika Anda menginginkannya untuk dimiliki oleh paket root Anda sendiri misalnya org.foo. Saya menemukan bahwa mengizinkan definisi untuk berada langsung di bawah paket yang seharusnya menjadi bagian dari - akan menjadi antarmuka api bahasa yang sedikit lebih tepat.
matanster
Perhatikan bahwa setidaknya sejak Scala 2.10 dan di atas overloading melakukan pekerjaan dalam paket benda.
Jasper-M
58

Sementara jawaban Moritz tepat, satu hal tambahan yang perlu diperhatikan adalah objek paket adalah objek. Antara lain, ini berarti Anda dapat membangunnya dari sifat-sifat, menggunakan warisan campuran. Contoh Moritz dapat ditulis sebagai

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 = // ...

}

Di sini Versioning adalah sifat abstrak, yang mengatakan bahwa objek paket harus memiliki metode "versi", sedangkan JodaAliases dan JavaAliases adalah sifat konkret yang berisi alias tipe praktis. Semua ciri ini dapat digunakan kembali oleh banyak objek paket yang berbeda.

Dave Griffith
sumber
Seluruh topik terbuka banyak dan tampaknya digunakan secara maksimal, terima kasih untuk contoh kaya lainnya.
Don Mackenzie
1
tetapi mereka tidak dapat digunakan sebagai vals, jadi mereka sebenarnya bukan objek
Eduardo Pareja Tobes
7

Anda bisa melakukan lebih buruk daripada langsung ke sumbernya. :)

https://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/library/scala/package.scala

https://lampsvn.epfl.ch/trac/scala/browser/scala/trunk/src/library/scala/collection/immutable/package.scala

Alex Cruise
sumber
@Alex Cruise, terima kasih, ini sepertinya menunjukkan bahwa mereka memerlukan unit kompilasi terpisah (yang mungkin mengatasi pembatasan paket yang dibatasi kurung kurawal). Masalahnya adalah saya menginginkan beberapa saran pengguna yang solid daripada dugaan saya sendiri tentang cara menggunakannya.
Don Mackenzie
5

Kasus penggunaan utama objek paket adalah saat Anda membutuhkan definisi di berbagai tempat di dalam paket Anda serta di luar paket saat Anda menggunakan API yang ditentukan oleh paket.

Tidak demikian halnya dengan Scala 3 yang rencananya akan dirilis pertengahan tahun 2020, berdasarkan Dotty , seperti di sini :

Definisi Toplevel

Segala macam definisi dapat ditulis di tingkat atas.
Objek paket tidak lagi diperlukan, akan dihapus secara bertahap.

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)
VonC
sumber
Terima kasih @VonC, saya sangat menantikan Scala 3 untuk ini dan banyak alasan lainnya. Saya belum banyak menggunakan objek paket tetapi saya yakin saya akan menggunakan definisi tingkat atas.
Don Mackenzie