อะไรคือข้อดีของการใช้คลาสนามธรรมแทนลักษณะ (นอกเหนือจากประสิทธิภาพ) ดูเหมือนว่าคลาสนามธรรมสามารถถูกแทนที่ด้วยลักษณะในกรณีส่วนใหญ่
อะไรคือข้อดีของการใช้คลาสนามธรรมแทนลักษณะ (นอกเหนือจากประสิทธิภาพ) ดูเหมือนว่าคลาสนามธรรมสามารถถูกแทนที่ด้วยลักษณะในกรณีส่วนใหญ่
คำตอบ:
ฉันสามารถนึกถึงความแตกต่างสองอย่าง
มีส่วนในการเขียนโปรแกรมใน Scala ที่เรียกว่า"ในลักษณะหรือไม่ต้องมีลักษณะ" ที่อยู่คำถามนี้ เนื่องจากรุ่นที่ 1 พร้อมใช้งานออนไลน์ฉันหวังว่าจะเป็นไปได้ที่จะเสนอราคาทั้งหมดที่นี่ (โปรแกรมเมอร์สกาล่าที่จริงจังควรซื้อหนังสือ):
เมื่อใดก็ตามที่คุณใช้คอลเลกชันของพฤติกรรมที่ใช้ซ้ำได้คุณจะต้องตัดสินใจว่าคุณต้องการใช้คุณลักษณะหรือคลาสนามธรรม ไม่มีกฎของ บริษัท แต่ส่วนนี้มีหลักเกณฑ์ที่ควรพิจารณา
หากพฤติกรรมจะไม่ถูกนำมาใช้ซ้ำแล้วทำให้มันเป็นระดับที่เป็นรูปธรรม มันไม่ใช่พฤติกรรมที่นำมาใช้ซ้ำได้หลังจากทั้งหมด
ถ้ามันอาจถูกนำมาใช้ซ้ำในหลายคลาสที่ไม่เกี่ยวข้องทำให้มันเป็นลักษณะ ลักษณะเฉพาะสามารถผสมเป็นส่วนต่าง ๆ ของลำดับชั้นของคลาส
หากคุณต้องการรับช่วงจากในโค้ด Javaให้ใช้คลาสนามธรรม เนื่องจากคุณสมบัติที่มีรหัสไม่มีอะนาล็อก Java แบบปิดดังนั้นจึงมีความแปลกที่จะสืบทอดจากลักษณะในคลาส Java ขณะที่การสืบทอดจากคลาส Scala นั้นเหมือนกับการสืบทอดจากคลาส Java เป็นข้อยกเว้นประการหนึ่งคุณลักษณะของ Scala ที่มีสมาชิกแบบนามธรรมเท่านั้นแปลโดยตรงไปยังส่วนต่อประสาน Java ดังนั้นคุณควรกำหนดลักษณะดังกล่าวแม้ว่าคุณจะคาดหวังว่ารหัส Java จะสืบทอดมาจากมันก็ตาม ดูบทที่ 29 สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการทำงานกับ Java และ Scala ด้วยกัน
หากคุณวางแผนที่จะแจกจ่ายในรูปแบบที่คอมไพล์และคุณคาดว่ากลุ่มภายนอกจะเขียนคลาสที่สืบทอดมาจากคลาสนั้นคุณอาจเรียนโดยใช้คลาสนามธรรม ปัญหาคือว่าเมื่อคุณลักษณะได้รับหรือสูญเสียสมาชิกคลาสใด ๆ ที่สืบทอดจากมันจะต้องถูกคอมไพล์ใหม่แม้ว่าพวกเขาจะไม่ได้เปลี่ยนก็ตาม หากลูกค้าภายนอกจะโทรเข้ามาในพฤติกรรมเท่านั้นแทนที่จะสืบทอดจากมันแล้วการใช้ลักษณะที่ดี
หากประสิทธิภาพมีความสำคัญมากคุณควรใช้คลาส Java runtimes ส่วนใหญ่ทำการเรียกใช้เมธอดเสมือนของสมาชิกคลาสเป็นการดำเนินการที่เร็วกว่าการเรียกใช้เมธอดอินเตอร์เฟส ลักษณะได้รับการรวบรวมไปยังอินเทอร์เฟซและดังนั้นอาจจ่ายค่าใช้จ่ายประสิทธิภาพเล็กน้อย อย่างไรก็ตามคุณควรเลือกตัวเลือกนี้เฉพาะเมื่อคุณรู้ว่าคุณลักษณะที่เป็นปัญหานั้นเป็นปัญหาคอขวดของการทำงานและมีหลักฐานว่าการใช้คลาสแทนแก้ปัญหาได้จริง
หากคุณยังไม่ทราบหลังจากพิจารณาข้างต้นแล้วเริ่มต้นด้วยการทำให้มันเป็นลักษณะ คุณสามารถเปลี่ยนได้ตลอดเวลาในภายหลังและโดยทั่วไปการใช้คุณลักษณะจะเปิดตัวเลือกเพิ่มเติม
ตามที่ @Mushtaq Ahmed ได้กล่าวถึงคุณลักษณะไม่สามารถส่งผ่านพารามิเตอร์ใด ๆ ไปยังตัวสร้างหลักของคลาสได้
super
แตกต่างก็คือการรักษา
ความแตกต่างอื่น ๆ ระหว่างคลาสและลักษณะคือในขณะที่ในคลาสการ
super
เรียกถูกผูกไว้แบบคงที่ในลักษณะพวกเขาถูกผูกไว้แบบไดนามิก ถ้าคุณเขียนsuper.toString
ในคลาสคุณรู้แน่ชัดว่าการใช้งานวิธีใดจะถูกเรียกใช้ เมื่อคุณเขียนสิ่งเดียวกันในลักษณะอย่างไรก็ตามการใช้เมธอดเพื่อเรียกใช้สำหรับการเรียก super จะไม่ได้กำหนดเมื่อคุณกำหนดลักษณะ
ดูส่วนที่เหลือของบทที่ 12สำหรับรายละเอียดเพิ่มเติม
แก้ไข 1 (2013):
มีความแตกต่างเล็กน้อยในวิธีการเรียนนามธรรมเมื่อเทียบกับลักษณะ หนึ่งในกฎการทำให้เป็นเชิงเส้นคือมันรักษาลำดับชั้นการสืบทอดของคลาสซึ่งมีแนวโน้มที่จะผลักดันคลาสนามธรรมในห่วงโซ่ในขณะที่ลักษณะสามารถผสมอย่างมีความสุขในบางสถานการณ์มันเป็นจริงดีกว่าที่จะอยู่ในตำแหน่งหลังของ ดังนั้นคลาสนามธรรมจึงสามารถใช้ได้ ดูข้อ จำกัด เชิงเส้นชั้น (สั่ง mixin) ใน Scala
แก้ไข 2 (2018):
ตั้งแต่ Scala 2.12 พฤติกรรมการทำงานร่วมกันของไบนารีของ trait ได้เปลี่ยนไป ก่อน 2.12 การเพิ่มหรือลบสมาชิกไปยังคุณลักษณะที่จำเป็นต้องคอมไพล์ใหม่ของคลาสทั้งหมดที่สืบทอดคุณสมบัติแม้ว่าคลาสจะไม่เปลี่ยนแปลง นี่เป็นเพราะลักษณะการเข้ารหัสใน JVM
ในฐานะของ Scala 2.12 ลักษณะการคอมไพล์ไปยังอินเทอร์เฟซของ Javaดังนั้นความต้องการจึงผ่อนคลายลงเล็กน้อย หากลักษณะดังกล่าวทำสิ่งใดสิ่งหนึ่งต่อไปนี้คลาสย่อยนั้นยังต้องการการคอมไพล์ใหม่:
- การกำหนดเขตข้อมูล (
val
หรือvar
แต่ค่าคงที่ก็โอเค -final val
ไม่มีประเภทผลลัพธ์)- โทร
super
- งบ initializer ในร่างกาย
- ขยายชั้นเรียน
- ขึ้นอยู่กับการทำให้เป็นเชิงเส้นเพื่อค้นหาการใช้งานใน supertrait ด้านขวา
แต่ถ้าคุณสมบัติไม่ได้ตอนนี้คุณสามารถปรับปรุงได้โดยไม่ทำลายความเข้ากันได้ของไบนารี
If outside clients will only call into the behavior, instead of inheriting from it, then using a trait is fine
- มีคนอธิบายความแตกต่างที่นี่ได้อย่างไร extends
VS with
?
extends
with
มันเป็นการสร้างประโยคอย่างหมดจด หากคุณได้รับมรดกจากหลายแม่แบบที่ได้รับครั้งแรกextend
, คนอื่น ๆ ทั้งหมดจะได้รับwith
ที่มัน คิดว่าwith
เป็นเครื่องหมายจุลภาค: class Foo extends Bar, Baz, Qux
.
สำหรับสิ่งที่คุ้มค่าการเขียนโปรแกรมของ Odersky et al ใน Scalaแนะนำว่าเมื่อคุณสงสัยคุณจะใช้คุณสมบัติ คุณสามารถเปลี่ยนเป็นคลาสนามธรรมได้ในภายหลังหากจำเป็น
นอกเหนือจากความจริงที่ว่าคุณไม่สามารถขยายคลาสนามธรรมหลายคลาสโดยตรง แต่คุณสามารถผสมหลาย ๆ คลาสเข้ากับคลาสได้มันเป็นสิ่งที่ควรค่าแก่การกล่าวถึงว่าคุณลักษณะนั้นสามารถวางซ้อนกันได้เนื่องจากการโทรสุดยอดในลักษณะนั้น ปัจจุบัน)
จากคำตอบของโทมัสในความแตกต่างระหว่างคลาสนามธรรมและคุณลักษณะ :
trait A{
def a = 1
}
trait X extends A{
override def a = {
println("X")
super.a
}
}
trait Y extends A{
override def a = {
println("Y")
super.a
}
}
scala> val xy = new AnyRef with X with Y
xy: java.lang.Object with X with Y = $anon$1@6e9b6a
scala> xy.a
Y
X
res0: Int = 1
scala> val yx = new AnyRef with Y with X
yx: java.lang.Object with Y with X = $anon$1@188c838
scala> yx.a
X
Y
res1: Int = 1
เมื่อขยายคลาสนามธรรมสิ่งนี้แสดงให้เห็นว่าคลาสย่อยมีลักษณะคล้ายกัน นี่ไม่ใช่กรณีที่จำเป็นเมื่อใช้คุณลักษณะฉันคิดว่า
ในการเขียนโปรแกรม Scalaผู้เขียนบอกว่าคลาสนามธรรมทำให้วัตถุคลาสสิกที่มุ่งเน้นความสัมพันธ์ "is-a" ในขณะที่ลักษณะเป็นสกาล่าทางขององค์ประกอบ
คลาสที่เป็นนามธรรมสามารถมีพฤติกรรมได้ - พวกมันสามารถกำหนดพารามิเตอร์ด้วยตัวสร้าง args (ซึ่งลักษณะไม่สามารถทำได้) และแสดงถึงเอนทิตีที่ใช้งานได้ ลักษณะแทนเพียงแค่แสดงคุณสมบัติเดียวเป็นอินเทอร์เฟซของฟังก์ชันการทำงานหนึ่ง
trait Enumerable
ด้วยฟังก์ชั่นผู้ช่วยจำนวนมากฉันจะไม่เรียกพวกเขาพฤติกรรมแต่เพียงฟังก์ชั่นการเชื่อมต่อกับคุณสมบัติเดียว