ทำความเข้าใจกับคำหลัก 'ประเภท' ใน Scala


144

ฉันยังใหม่กับสกาล่าและฉันไม่สามารถหาtypeคำหลักได้มากนัก ฉันพยายามเข้าใจว่านิพจน์ต่อไปนี้อาจหมายถึงอะไร:

type FunctorType = (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate

FunctorType นามแฝงบางประเภท แต่มีความหมายว่าอะไร

คำตอบ:


148

ใช่นามแฝงประเภท FunctorTypeเป็นเพียงชวเลข

(LocalDate, HolidayCalendar, Int, Boolean) => LocalDate

ประเภทนามแฝงมักจะใช้เพื่อให้รหัสที่เหลือง่าย: ตอนนี้คุณสามารถเขียน

def doSomeThing(f: FunctorType)

ซึ่งจะแปลโดยคอมไพเลอร์เป็น

def doSomeThing(f: (LocalDate, HolidayCalendar, Int, Boolean) => LocalDate)

สิ่งนี้ช่วยในการหลีกเลี่ยงการกำหนดประเภทที่กำหนดเองมากมายที่เป็นเพียง tuples หรือฟังก์ชั่นที่กำหนดไว้ในประเภทอื่นเช่น

นอกจากนี้ยังมีกรณีการใช้งานที่น่าสนใจอื่น ๆ อีกหลายกรณีtypeดังที่อธิบายไว้ในบทของการเขียนโปรแกรมใน Scalaนี้


198

จริงๆแล้วtypeคำหลักใน Scala สามารถทำได้มากกว่าแค่การใช้นามแฝงประเภทที่ซับซ้อนไปเป็นชื่อที่สั้นกว่า แนะนำสมาชิกประเภท

ดังที่คุณทราบคลาสสามารถมีสมาชิกฟิลด์และสมาชิกเมธอด สกาล่ายังให้ชั้นเรียนมีสมาชิกประเภทด้วย

ในกรณีเฉพาะของคุณtypeคือการแนะนำนามแฝงที่ช่วยให้คุณเขียนโค้ดที่กระชับยิ่งขึ้น ระบบประเภทเพียงแทนที่นามแฝงด้วยประเภทจริงเมื่อดำเนินการตรวจสอบประเภท

แต่คุณสามารถมีอะไรเช่นนี้

trait Base {
  type T

  def method: T
}

class Implementation extends Base {
  type T = Int

  def method: T = 42
}

เช่นเดียวกับสมาชิกคนอื่น ๆ ของคลาสสมาชิกประเภทยังสามารถเป็นนามธรรม (คุณเพียงแค่ไม่ได้ระบุค่าที่แท้จริงของพวกเขา) และสามารถถูกแทนที่ในการใช้งาน

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

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

โปรดดูคำตอบที่ยอดเยี่ยมสำหรับรายละเอียดเพิ่มเติม:

Scala: บทคัดย่อประเภทเทียบกับยาชื่อสามัญ


44
สิ่งสำคัญคือต้องจำไว้ว่าการใช้ประเภทภายในคลาสสร้างสมาชิกประเภทแทนนามแฝง ดังนั้นหากคุณต้องการเพียงนามแฝงประเภทให้กำหนดไว้ในวัตถุที่แสดงร่วม
Rüdiger Klaehn

9

ฉันชอบคำตอบจาก Roland Ewaldตั้งแต่เขาอธิบายด้วยกรณีการใช้งานง่าย ๆ ของ alias ประเภทและสำหรับรายละเอียดเพิ่มเติมแนะนำการกวดวิชาที่ดีมาก อย่างไรก็ตามเนื่องจากมีการแนะนำกรณีการใช้งานอื่นในโพสต์นี้ชื่อประเภทสมาชิกฉันต้องการพูดถึงกรณีการใช้งานจริงมากที่สุดของมันซึ่งฉันชอบมาก: (ส่วนนี้นำมาจากที่นี่ :)

ประเภทนามธรรม:

type T

T ด้านบนบอกว่าประเภทนี้ที่จะใช้ไม่เป็นที่รู้จักและขึ้นอยู่กับคลาสย่อยของคอนกรีตมันจะถูกกำหนด วิธีที่ดีที่สุดในการทำความเข้าใจแนวคิดการเขียนโปรแกรมให้ตัวอย่าง: สมมติว่าคุณมีสถานการณ์ต่อไปนี้:

ไม่มีประเภทนามธรรม

ที่นี่คุณจะได้รับข้อผิดพลาดในการคอมไพล์เนื่องจากวิธีการกินในคลาส Cow และ Tiger จะไม่แทนที่วิธีการกินในคลาส Animal เนื่องจากชนิดพารามิเตอร์แตกต่างกัน มันเป็นหญ้าในวัวชั้นและเนื้อในชั้นเสือกับอาหารในชั้นเรียนสัตว์ซึ่งเป็นซุปเปอร์คลาสและคลาสย่อยทั้งหมดต้องเป็นไปตาม

ตอนนี้กลับไปที่ประเภท abstraction โดยไดอะแกรมต่อไปนี้และเพียงเพิ่ม abstraction ประเภทคุณสามารถกำหนดประเภทของอินพุตในคลาสย่อยเองได้

ด้วยประเภทบทคัดย่อ

ตอนนี้ดูรหัสต่อไปนี้:

  val cow1: Cow = new Cow
  val cow2: Cow = new Cow

  cow1 eat new cow1.SuitableFood
  cow2 eat new cow1.SuitableFood

  val tiger: Tiger = new Tiger
  cow1 eat new tiger.SuitableFood // Compiler error

คอมไพเลอร์มีความสุขและเราปรับปรุงการออกแบบของเรา เราสามารถเลี้ยงวัวของเราด้วยวัวเหมาะสำหรับอาหารและคอมไพเลอร์ป้องกันไม่ให้เราเลี้ยงวัวด้วยอาหารที่เหมาะสมกับเสือ แต่ถ้าเราต้องการสร้างความแตกต่างระหว่างชนิดของวัว 1 อาหารที่เหมาะสมและอาหารวัว 2 กล่าวอีกนัยหนึ่งมันจะมีประโยชน์มากในบางสถานการณ์หากเส้นทางที่เราไปถึงประเภท (แน่นอนผ่านทางวัตถุ) นั้นสำคัญ ด้วยคุณสมบัติขั้นสูงในสกาล่ามันเป็นไปได้:

ประเภทพา ธ ที่ขึ้นอยู่กับ: วัตถุสกาล่าสามารถมีประเภทเป็นสมาชิก ความหมายของประเภทขึ้นอยู่กับเส้นทางที่คุณใช้ในการเข้าถึง เส้นทางถูกกำหนดโดยการอ้างอิงไปยังวัตถุ (aka ตัวอย่างของคลาส) ในการใช้สถานการณ์นี้คุณต้องกำหนดคลาส Grass ภายในวัวเช่น Cow คือคลาสภายนอกและ Grass เป็นคลาสภายใน โครงสร้างจะเป็นดังนี้:

  class Cow extends Animal {
    class Grass extends Food
    type SuitableFood = Grass
    override def eat(food: this.SuitableFood): Unit = {}
  }

  class Tiger extends Animal {
    class Meat extends Food
    type SuitableFood = Meat
    override def eat(food: this.SuitableFood): Unit = {}
  }

ตอนนี้ถ้าคุณพยายามที่จะรวบรวมรหัสนี้:

  1. val cow1: Cow = new Cow
  2. val cow2: Cow = new Cow

  3. cow1 eat new cow1.SuitableFood
  4. cow2 eat new cow1.SuitableFood // compilation error

ในบรรทัดที่ 4 คุณจะเห็นข้อผิดพลาดเนื่องจาก Grass เป็นคลาสภายในของ Cow ดังนั้นในการสร้างตัวอย่างของ Grass เราจำเป็นต้องมีวัตถุวัวและวัตถุวัวนี้จะกำหนดเส้นทาง ดังนั้นวัตถุ 2 ตัวทำให้เกิดเส้นทางที่แตกต่างกัน 2 แบบ ในสถานการณ์นี้ Cow2 เพียงต้องการกินอาหารที่สร้างขึ้นโดยเฉพาะสำหรับมัน ดังนั้น:

cow2 eat new cow2.SuitableFood

ตอนนี้ทุกคนมีความสุข :-)


5

เพียงตัวอย่างเพื่อดูวิธีใช้ "type" เป็นนามแฝง:

type Action = () => Unit

คำนิยามข้างต้นกำหนดการกระทำที่จะเป็นนามแฝงของประเภทของขั้นตอน (วิธีการ) ที่ใช้รายการพารามิเตอร์ที่ว่างเปล่าและหน่วยที่กลับมา

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.