แพ็คเกจวัตถุ


92

แพคเกจวัตถุคืออะไรไม่ใช่แนวคิดมากนัก แต่เป็นการใช้งาน

ฉันพยายามหาตัวอย่างที่ใช้งานได้และรูปแบบเดียวที่ฉันต้องทำงานมีดังนี้:

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

ข้อสังเกตที่ฉันได้ทำคือ:

package object _root_ { ... }

ไม่ได้รับอนุญาต (ซึ่งสมเหตุสมผล)

package object x.y { ... }

ยังไม่ได้รับอนุญาต

ดูเหมือนว่าจะต้องประกาศอ็อบเจ็กต์แพ็กเกจในแพ็กเกจพาเรนต์ทันทีและหากเขียนตามด้านบนจำเป็นต้องใช้แบบฟอร์มการประกาศแพ็กเกจที่คั่นด้วยวงเล็บปีกกา

ใช้กันทั่วไปหรือไม่? ถ้าเป็นเช่นนั้นอย่างไร?



1
@ เบรนต์นี่เป็นแหล่งข้อมูลที่ยอดเยี่ยมไม่ใช่เฉพาะสำหรับบทความวัตถุแพ็คเกจ ฉันเคยได้ยินชื่อผู้เขียน แต่ไม่รู้ว่าเขาเขียนทัวร์ Scala นี้ขอบคุณ
Don Mackenzie

คำตอบ:


128

โดยปกติคุณจะใส่วัตถุแพ็กเกจของคุณในไฟล์แยกต่างหากที่เรียกว่าpackage.scalaในแพ็กเกจที่สอดคล้องกับ คุณยังสามารถใช้ไวยากรณ์ของแพ็กเกจที่ซ้อนกันได้ แต่มันค่อนข้างผิดปกติ

กรณีการใช้งานหลักสำหรับอ็อบเจ็กต์แพ็กเกจคือเมื่อคุณต้องการคำจำกัดความในที่ต่างๆภายในแพ็กเกจของคุณและภายนอกแพ็กเกจเมื่อคุณใช้ API ที่กำหนดโดยแพ็กเกจ นี่คือตัวอย่าง:

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

}

foo.barตอนนี้คำจำกัดความภายในวัตถุแพคเกจที่มีอยู่ภายในแพคเกจทั้งหมด นอกจากนี้คำจำกัดความจะถูกอิมพอร์ตเมื่อมีบุคคลภายนอกนำเข้าแพ็กเกจfoo.bar._นั้น

ด้วยวิธีนี้คุณสามารถป้องกันไม่ให้ไคลเอ็นต์ API ออกการนำเข้าเพิ่มเติมเพื่อใช้ไลบรารีของคุณอย่างมีประสิทธิภาพเช่นใน scala-swing คุณต้องเขียน

import swing._
import Swing._

ที่จะมีความดีทุกอย่างเหมือนonEDTและการแปลงโดยปริยายจากไปTuple2Dimension


13
คำเตือน: การโอเวอร์โหลดวิธีใช้ไม่ได้ในวัตถุแพคเกจ
retronym

เต้นฉันว่าทำไมถึงเลือกที่จะกำหนดอ็อบเจ็กต์แพ็กเกจขึ้นหนึ่งระดับบนลำดับชั้นของแพ็กเกจ เช่นนี้หมายความว่าคุณต้องสร้างมลพิษให้กับแพ็คเกจเสมือนorgหรือcomระดับบนสุดด้วยออบเจ็กต์แพ็คเกจของคุณหากคุณต้องการให้มันเป็นของแพ็คเกจรูทของorg.fooคุณ ฉันพบว่าการอนุญาตให้คำจำกัดความอยู่ภายใต้แพ็คเกจโดยตรงมันควรเป็นส่วนหนึ่งของ - จะเป็นอินเตอร์เฟส api ภาษาที่เหมาะสมกว่าเล็กน้อย
matanster

โปรดทราบว่าเนื่องจากอย่างน้อย Scala 2.10 ขึ้นไปการโอเวอร์โหลดจะทำงานในอ็อบเจ็กต์แพ็กเกจ
Jasper-M

58

ในขณะที่คำตอบของมอริทซ์เป็นสิ่งที่ควรทราบอีกประการหนึ่งคือวัตถุบรรจุภัณฑ์เป็นวัตถุ เหนือสิ่งอื่นใดนั่นหมายความว่าคุณสามารถสร้างมันขึ้นมาจากลักษณะโดยใช้การสืบทอดแบบผสมผสาน ตัวอย่างของ Moritz สามารถเขียนเป็น

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

}

ที่นี่ Versioning เป็นลักษณะนามธรรมซึ่งบอกว่าอ็อบเจ็กต์แพ็กเกจต้องมีเมธอด "เวอร์ชัน" ในขณะที่ JodaAliases และ JavaAliases เป็นลักษณะที่เป็นรูปธรรมที่มีนามแฝงประเภทที่ใช้งานง่าย ลักษณะทั้งหมดนี้สามารถใช้ซ้ำได้โดยอ็อบเจ็กต์แพ็คเกจต่างๆ


หัวข้อทั้งหมดกำลังเปิดกว้างขึ้นมากและดูเหมือนว่าจะถูกใช้จนเต็มศักยภาพขอบคุณสำหรับอีกตัวอย่าง
Don Mackenzie

1
แต่ไม่สามารถใช้เป็น vals ได้ดังนั้นจึงไม่ใช่วัตถุจริงๆ
Eduardo Pareja Tobes

7

@Alex Cruise ขอบคุณดูเหมือนว่าจะแนะนำว่าพวกเขาต้องการหน่วยคอมไพล์แยกต่างหาก ปัญหาคือฉันต้องการคำแนะนำจากผู้ใช้ที่มั่นคงมากกว่าการคาดเดาของฉันเองเกี่ยวกับวิธีใช้
Don Mackenzie

5

กรณีการใช้งานหลักสำหรับอ็อบเจ็กต์แพ็กเกจคือเมื่อคุณต้องการคำจำกัดความในที่ต่างๆภายในแพ็กเกจของคุณและภายนอกแพ็กเกจเมื่อคุณใช้ API ที่กำหนดโดยแพ็กเกจ

ไม่เป็นเช่นนั้นกับScala 3ซึ่งมีกำหนดเปิดตัวในช่วงกลางปี ​​2020 โดยอ้างอิงจากDottyดังที่นี่ :

คำจำกัดความของ Toplevel

คำจำกัดความทุกประเภทสามารถเขียนบน toplevel ได้
ไม่จำเป็นต้องใช้วัตถุในแพ็คเกจอีกต่อไปจะถูกยกเลิก

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 ฉันรอคอย Scala 3 สำหรับเรื่องนี้และเหตุผลอื่น ๆ อีกมากมาย ฉันไม่ได้ใช้ประโยชน์จากวัตถุแพ็คเกจมากนัก แต่ฉันแน่ใจว่าจะใช้คำจำกัดความระดับบนสุด
Don Mackenzie
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.