The Dart Language: เมื่อ Java และ C# ยังไม่คมชัดเพียงพอ

เผยแพร่แล้ว: 2022-03-11

ย้อนกลับไปในปี 2013 การเปิดตัวเวอร์ชัน 1.0 อย่างเป็นทางการของ Dart ได้รับการเผยแพร่ เช่นเดียวกับข้อเสนอของ Google ส่วนใหญ่ แต่ไม่ใช่ทุกคนที่กระตือรือร้นเท่าทีมภายในของ Google ในการสร้างแอปที่มีความสำคัญต่อธุรกิจด้วยภาษา Dart ด้วยการสร้างใหม่ของ Dart 2 ที่รอบคอบในห้าปีต่อมา Google ดูเหมือนจะพิสูจน์ให้เห็นถึงความมุ่งมั่นในด้านภาษา อันที่จริง ทุกวันนี้ นักพัฒนายังคงได้รับแรงฉุดลากอย่างต่อเนื่อง โดยเฉพาะ Java และ C#

ภาษาโปรแกรม Dart มีความสำคัญด้วยเหตุผลบางประการ:

  • มีสิ่งที่ดีที่สุดของทั้งสองโลก: เป็นภาษาที่คอมไพล์แล้ว ปลอดภัยสำหรับการพิมพ์ (เช่น C# และ Java) และภาษาสคริปต์ (เช่น Python และ JavaScript) ในเวลาเดียวกัน
  • มันแปลงเป็น JavaScript เพื่อใช้เป็นส่วนหน้าของเว็บ
  • มันทำงานบนทุกสิ่ง และคอมไพล์ไปยังแอพมือถือที่มาพร้อมเครื่อง คุณจึงสามารถใช้งานได้เกือบทุกอย่าง
  • Dart นั้นคล้ายกับ C# และ Java ในไวยากรณ์ ดังนั้นจึงเรียนรู้ได้อย่างรวดเร็ว

พวกเราจากโลก C# หรือ Java ของระบบองค์กรขนาดใหญ่รู้อยู่แล้วว่าทำไมความปลอดภัยของประเภท ข้อผิดพลาดในการคอมไพล์ และ Linters จึงมีความสำคัญ พวกเราหลายคนลังเลที่จะใช้ภาษา "สคริปต์" เพราะกลัวว่าจะสูญเสียโครงสร้าง ความเร็ว ความแม่นยำ และความสามารถในการแก้ไขข้อบกพร่องทั้งหมดที่เราคุ้นเคย

แต่ด้วยการพัฒนา Dart เราไม่ต้องละทิ้งสิ่งนั้น เราสามารถเขียนแอพมือถือ เว็บไคลเอนต์ และแบ็คเอนด์ในภาษาเดียวกัน—และรับทุกสิ่งที่เรายังคงชื่นชอบเกี่ยวกับ Java และ C#!

ในตอนท้าย เรามาดูตัวอย่างภาษา Dart ที่สำคัญซึ่งน่าจะใหม่สำหรับนักพัฒนา C# หรือ Java ซึ่งเราจะสรุปเป็น PDF ภาษา Dart ในตอนท้าย

หมายเหตุ: บทความนี้ครอบคลุมเฉพาะ Dart 2.x เวอร์ชัน 1.x ไม่ได้ "ปรุงเต็มที่" - โดยเฉพาะระบบประเภทเป็นคำแนะนำ (เช่น TypeScript) แทนที่จะเป็นที่จำเป็น (เช่น C# หรือ Java)

1. องค์กรรหัส

อันดับแรก เราจะพูดถึงความแตกต่างที่สำคัญที่สุดอย่างหนึ่ง นั่นคือ วิธีจัดระเบียบและอ้างอิงไฟล์โค้ด

ไฟล์ต้นฉบับ ขอบเขต เนมสเปซ และการนำเข้า

ใน C # คอลเล็กชันของคลาสจะถูกคอมไพล์ไปยังแอสเซมบลี แต่ละคลาสมีเนมสเปซ และบ่อยครั้งเนมสเปซสะท้อนถึงการจัดระเบียบของซอร์สโค้ดในระบบไฟล์—แต่ในท้ายที่สุด แอสเซมบลีจะไม่เก็บข้อมูลใดๆ เกี่ยวกับตำแหน่งไฟล์ซอร์สโค้ด

ใน Java ไฟล์ต้นฉบับเป็นส่วนหนึ่งของแพ็คเกจและเนมสเปซมักจะสอดคล้องกับตำแหน่งระบบไฟล์ แต่ในท้ายที่สุด แพ็คเกจเป็นเพียงคอลเล็กชันของคลาส

ดังนั้นทั้งสองภาษาจึงมีวิธีทำให้ซอร์สโค้ดไม่ขึ้นกับระบบไฟล์

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

ดังนั้น คุณจะต้องเปลี่ยนความคิดจาก "กลุ่มของชั้นเรียน" เป็น "ลำดับของไฟล์โค้ดที่รวมอยู่"

Dart รองรับทั้งการจัดแพ็คเกจและองค์กรเฉพาะกิจที่ไม่มีแพ็คเกจ เริ่มต้นด้วยตัวอย่างที่ไม่มีแพ็คเกจเพื่อแสดงลำดับของไฟล์ที่รวม:

 // file1.dart int alice = 1; // top level variable int barry() => 2; // top level function var student = Charlie(); // top level variable; Charlie is declared below but that's OK class Charlie { ... } // top level class // alice = 2; // top level statement not allowed // file2.dart import 'file1.dart'; // causes all of file1 to be in scope main() { print(alice); // 1 }

ทุกสิ่งที่คุณอ้างถึงในไฟล์ต้นทางจะต้องมีการประกาศหรือนำเข้าภายในไฟล์นั้น เนื่องจากไม่มีระดับ "โครงการ" และไม่มีวิธีอื่นใดในการรวมองค์ประกอบต้นทางอื่นๆ ไว้ในขอบเขต

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

 // file2.dart import 'file1.dart' as wonderland; main() { print(wonderland.alice); // 1 }

แพ็คเกจ

ตัวอย่างด้านบนจัดระเบียบโค้ดโดยไม่มีแพ็คเกจ ในการใช้แพ็คเกจ โค้ดจะได้รับการจัดระเบียบอย่างเฉพาะเจาะจงมากขึ้น นี่คือตัวอย่างเค้าโครงแพ็คเกจสำหรับแพ็คเกจที่ชื่อ apples :

  • apples/
    • pubspec.yaml — กำหนดชื่อแพ็คเกจ การขึ้นต่อกัน และสิ่งอื่น ๆ
    • lib/
      • apples.dart นำเข้าและส่งออก; นี่คือไฟล์ที่นำเข้าโดยผู้บริโภคของแพ็คเกจ
      • src/
        • seeds.dart —โค้ดอื่นๆ ทั้งหมดที่นี่
    • bin/
      • runapples.dart —มีฟังก์ชันหลักซึ่งเป็นจุดเริ่มต้น (หากเป็นแพ็คเกจที่รันได้หรือรวมเครื่องมือที่รันได้)

จากนั้นคุณสามารถนำเข้าทั้งแพ็คเกจแทนแต่ละไฟล์:

 import 'package:apples';

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

2. ประเภทข้อมูล

มีความแตกต่างที่สำคัญในระบบประเภทของ Dart ที่ต้องระวัง เกี่ยวกับค่า Null ประเภทตัวเลข คอลเลกชั่น และประเภทไดนามิก

Nulls ทุกที่

มาจาก C # หรือ Java เราคุ้นเคยกับประเภท ดั้งเดิม หรือ ค่า ที่แตกต่างจากประเภท อ้างอิง หรือ วัตถุ ในทางปฏิบัติ ประเภทของค่าจะถูกจัดสรรบนสแต็กหรือในรีจิสเตอร์ และสำเนาของค่าจะถูกส่งเป็นพารามิเตอร์ของฟังก์ชัน ประเภทการอ้างอิงจะถูกจัดสรรบนฮีปแทน และส่งเฉพาะตัวชี้ไปยังอ็อบเจ็กต์เป็นพารามิเตอร์ของฟังก์ชัน เนื่องจากประเภทค่าจะใช้หน่วยความจำเสมอ ตัวแปรประเภทค่าจึงไม่สามารถเป็นค่าว่างได้ และสมาชิกประเภทค่าทั้งหมดต้องมีค่าเริ่มต้น

โผขจัดความแตกต่างนั้นเพราะทุกสิ่งเป็นวัตถุ ทุกประเภทได้มาจากประเภท Object ในที่สุด ดังนั้น สิ่งนี้จึงถูกกฎหมาย:

 int i = null;

อันที่จริง primitives ทั้งหมดถูกกำหนดโดยปริยายเป็น null ซึ่งหมายความว่าคุณไม่สามารถสรุปได้ว่าค่าเริ่มต้นของจำนวนเต็มเป็นศูนย์ตามที่คุณคุ้นเคยใน C# หรือ Java และคุณอาจต้องเพิ่มการตรวจสอบค่าว่าง

ที่น่าสนใจคือแม้แต่ Null ก็เป็นประเภทหนึ่ง และคำว่า null หมายถึงอินสแตนซ์ของ Null :

 print(null.runtimeType); // prints Null

มีตัวเลขไม่มากนัก

ต่างจากการแบ่งประเภทจำนวนเต็มที่คุ้นเคยตั้งแต่ 8 ถึง 64 บิตที่มีการลงนามและไม่ได้ลงนาม ประเภทจำนวนเต็มหลักของ Dart เป็นเพียง int ซึ่งเป็นค่า 64 บิต (นอกจากนี้ยังมี BigInt สำหรับตัวเลขที่มีขนาดใหญ่มาก)

เนื่องจากไม่มีอาร์เรย์ไบต์เป็นส่วนหนึ่งของไวยากรณ์ภาษา เนื้อหาไฟล์ไบนารีจึงสามารถประมวลผลเป็นรายการของจำนวนเต็มได้ เช่น List<Int>

หากคุณกำลังคิดว่าสิ่งนี้จะต้องไม่มีประสิทธิภาพอย่างมาก นักออกแบบก็คิดไว้แล้ว ในทางปฏิบัติ มีการแทนค่าภายในที่แตกต่างกัน ขึ้นอยู่กับค่าจำนวนเต็มจริงที่ใช้ขณะรันไทม์ รันไทม์ไม่ได้จัดสรรหน่วยความจำฮีปสำหรับอ็อบเจ็กต์ int ถ้ามันสามารถเพิ่มประสิทธิภาพนั้นออกไปและใช้การลงทะเบียน CPU ในโหมดที่ไม่ได้บรรจุในกล่อง นอกจากนี้ ไลบรารี byte_data ยังเสนอ UInt8List และการแสดงค่าอื่นๆ ที่ปรับให้เหมาะสมที่สุด

ของสะสม

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

นอกจากนี้ยังมีการสนับสนุน syntax สำหรับการเริ่มต้นคอลเลกชันสามประเภท:

 final a = [1, 2, 3]; // inferred type is List<int>, an array-like ordered collection final b = {1, 2, 3}; // inferred type is Set<int>, an unordered collection final c = {'a': 1, 'b': 2}; // inferred type is Map<string, int>, an unordered collection of name-value pairs

ดังนั้น ใช้ Dart List ที่คุณจะใช้ Java array, ArrayList หรือ Vector ; หรืออาร์เรย์ C# หรือ List ใช้ Set ที่คุณจะใช้ Java/C# HashSet ใช้ Map ที่คุณจะใช้ Java HashMap หรือ C# Dictionary

3. การพิมพ์แบบไดนามิกและแบบคงที่

ในภาษาแบบไดนามิก เช่น JavaScript, Ruby และ Python คุณสามารถอ้างอิงถึงสมาชิกได้แม้ว่าจะไม่มีอยู่จริงก็ตาม นี่คือตัวอย่าง JavaScript:

 var person = {}; // create an empty object person.name = 'alice'; // add a member to the object if (person.age < 21) { // refer to a property that is not in the object // ... }

หากคุณเรียกใช้สิ่งนี้ person.age จะเป็น undefined แต่จะรันต่อไป

ในทำนองเดียวกัน คุณสามารถเปลี่ยนประเภทของตัวแปรใน JavaScript:

 var a = 1; // a is a number a = 'one'; // a is now a string

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

 var b = 1; // a is an int // b = "one"; // not allowed in Java

Java อนุญาตให้คุณเขียนโค้ดด้วยประเภทสแตติกเท่านั้น (คุณสามารถใช้วิปัสสนาเพื่อทำพฤติกรรมไดนามิกบางอย่างได้ แต่ไม่ใช่ส่วนหนึ่งของไวยากรณ์โดยตรง) JavaScript และภาษาไดนามิกล้วนอื่นๆ อนุญาตให้คุณเขียนโค้ดด้วยประเภทไดนามิกเท่านั้น

ภาษา Dart อนุญาตทั้ง:

 // dart dynamic a = 1; // a is an int - dynamic typing a = 'one'; // a is now a string a.foo(); // we can call a function on a dynamic object, to be resolved at run time var b = 1; // b is an int - static typing // b = 'one'; // not allowed in Dart

Dart มี dynamic หลอกซึ่งทำให้ลอจิกประเภททั้งหมดได้รับการจัดการที่รันไทม์ ความพยายามที่จะเรียก a.foo() จะไม่รบกวนตัววิเคราะห์สแตติกและโค้ดจะทำงาน แต่จะล้มเหลวขณะรันไทม์เนื่องจากไม่มีวิธีการดังกล่าว

แต่เดิม C# นั้นเหมือนกับ Java และต่อมาได้เพิ่มการรองรับไดนามิก ดังนั้น Dart และ C# จึงใกล้เคียงกันในเรื่องนี้

4. ฟังก์ชั่น

ไวยากรณ์การประกาศฟังก์ชัน

ไวยากรณ์ของฟังก์ชันใน Dart นั้นเบากว่าเล็กน้อยและสนุกกว่าใน C # หรือ Java ไวยากรณ์เป็นอย่างใดอย่างหนึ่งเหล่านี้:

 // functions as declarations return-type name (parameters) {body} return-type name (parameters) => expression; // function expressions (assignable to variables, etc.) (parameters) {body} (parameters) => expression

ตัวอย่างเช่น:

 void printFoo() { print('foo'); }; String embellish(String s) => s.toUpperCase() + '!!'; var printFoo = () { print('foo'); }; var embellish = (String s) => s.toUpperCase() + '!!';

พารามิเตอร์ผ่าน

เนื่องจากทุกอย่างเป็นอ็อบเจ็กต์ รวมถึง primitives เช่น int และ String การส่งพารามิเตอร์อาจสร้างความสับสน แม้ว่าจะไม่มีพารามิเตอร์ ref ที่ส่งผ่านเหมือนใน C# แต่ทุกอย่างจะถูกส่งผ่านโดยการอ้างอิง และฟังก์ชันไม่สามารถเปลี่ยนการอ้างอิงของผู้โทรได้ เนื่องจากอ็อบเจ็กต์จะไม่ถูกโคลนเมื่อส่งผ่านไปยังฟังก์ชัน ฟังก์ชันอาจเปลี่ยนคุณสมบัติของอ็อบเจ็กต์ อย่างไรก็ตาม ความแตกต่างสำหรับ primitives เช่น int และ String นั้นเป็นสิ่งที่น่าสงสัยอย่างมีประสิทธิภาพเนื่องจากประเภทเหล่านั้นจะไม่เปลี่ยนรูป

 var id = 1; var name = 'alice'; var client = Client(); void foo(int id, String name, Client client) { id = 2; // local var points to different int instance name = 'bob'; // local var points to different String instance client.State = 'AK'; // property of caller's object is changed } foo(id, name, client); // id == 1, name == 'alice', client.State == 'AK'

พารามิเตอร์เสริม

หากคุณอยู่ในโลก C# หรือ Java คุณอาจสาปแช่งในสถานการณ์ด้วยวิธีโอเวอร์โหลดที่สับสนเช่นนี้:

 // java void foo(string arg1) {...} void foo(int arg1, string arg2) {...} void foo(string arg1, Client arg2) {...} // call site: foo(clientId, input3); // confusing! too easy to misread which overload it is calling

หรือด้วยพารามิเตอร์ทางเลือก C# มีความสับสนอีกประเภทหนึ่ง:

 // c# void Foo(string arg1, int arg2 = 0) {...} void Foo(string arg1, int arg3 = 0, int arg2 = 0) {...} // call site: Foo("alice", 7); // legal but confusing! too easy to misread which overload it is calling and which parameter binds to argument 7 Foo("alice", arg2: 9); // better

C# ไม่ต้องการการตั้งชื่ออาร์กิวเมนต์ทางเลือกที่ไซต์การโทร ดังนั้นการปรับโครงสร้างด้วยพารามิเตอร์ทางเลือกอาจเป็นอันตรายได้ หากไซต์การโทรบางแห่งถูกกฎหมายหลังจากรีแฟคเตอร์ คอมไพเลอร์จะไม่สามารถตรวจจับไซต์เหล่านั้นได้

Dart มีวิธีที่ปลอดภัยและยืดหยุ่นมาก ประการแรก ไม่ รองรับวิธีการโอเวอร์โหลด มีสองวิธีในการจัดการพารามิเตอร์ทางเลือกแทน:

 // positional optional parameters void foo(string arg1, [int arg2 = 0, int arg3 = 0]) {...} // call site for positional optional parameters foo('alice'); // legal foo('alice', 12); // legal foo('alice', 12, 13); // legal // named optional parameters void bar(string arg1, {int arg2 = 0, int arg3 = 0}) {...} bar('alice'); // legal bar('alice', arg3: 12); // legal bar('alice', arg3: 12, arg2: 13); // legal; sequence can vary and names are required

คุณไม่สามารถใช้ทั้งสองสไตล์ในการประกาศฟังก์ชันเดียวกันได้

ตำแหน่งคีย์เวิร์ด async

C # มีตำแหน่งที่สับสนสำหรับคีย์เวิร์ด async :

 Task<int> Foo() {...} async Task<int> Foo() {...}

นี่หมายความว่าลายเซ็นของฟังก์ชันเป็นแบบอะซิงโครนัส แต่จริงๆ แล้วเฉพาะการใช้งานฟังก์ชันเท่านั้นที่ไม่ซิงโครนัส ลายเซ็นใดลายเซ็นหนึ่งข้างต้นจะเป็นการใช้งานอินเทอร์เฟซนี้อย่างถูกต้อง:

 interface ICanFoo { Task<int> Foo(); }

ในภาษา Dart นั้น async จะอยู่ในตำแหน่งที่สมเหตุสมผลมากกว่า ซึ่งแสดงว่าการใช้งานนั้นไม่พร้อมกัน:

 Future<int> foo() async {...}

ขอบเขตและการปิด

เช่นเดียวกับ C # และ Java Dart มีขอบเขตคำศัพท์ ซึ่งหมายความว่าตัวแปรที่ประกาศในบล็อกนั้นอยู่นอกขอบเขตที่ส่วนท้ายของบล็อก ดังนั้น Dart จัดการกับการปิดในลักษณะเดียวกัน

ไวยากรณ์คุณสมบัติ

Java ทำให้รูปแบบ get/set เป็นที่นิยม แต่ภาษาไม่มีไวยากรณ์พิเศษใดๆ สำหรับมัน:

 // java private String clientName; public String getClientName() { return clientName; } public void setClientName(String value}{ clientName = value; }

C # มีไวยากรณ์สำหรับมัน:

 // c# private string clientName; public string ClientName { get { return clientName; } set { clientName = value; } }

Dart มีคุณสมบัติรองรับไวยากรณ์ที่แตกต่างกันเล็กน้อย:

 // dart string _clientName; string get ClientName => _clientName; string set ClientName(string s) { _clientName = s; }

5. ตัวสร้าง

ตัวสร้าง Dart มีความยืดหยุ่นมากกว่าใน C # หรือ Java เล็กน้อย คุณลักษณะที่ดีอย่างหนึ่งคือความสามารถในการตั้งชื่อคอนสตรัคเตอร์ที่แตกต่างกันในคลาสเดียวกัน:

 class Point { Point(double x, double y) {...} // default ctor Point.asPolar(double angle, double r) {...} // named ctor }

คุณสามารถเรียกคอนสตรัคเตอร์เริ่มต้นได้ด้วยชื่อคลาส: var c = Client();

มีการจดชวเลขสองประเภทสำหรับการเริ่มต้นสมาชิกของอินสแตนซ์ก่อนที่จะเรียกตัวสร้างเนื้อหา:

 class Client { String _code; String _name; Client(String this._name) // "this" shorthand for assigning parameter to instance member : _code = _name.toUpper() { // special out-of-body place for initializing // body } }

ตัวสร้างสามารถเรียกใช้ตัวสร้าง superclass และเปลี่ยนเส้นทางไปยังตัวสร้างอื่นในคลาสเดียวกัน:

 Foo.constructor1(int x) : this(x); // redirect to the default ctor in same class; no body allowed Foo.constructor2(int x) : super.plain(x) {...} // call base class named ctor, then run this body Foo.constructor3(int x) : _b = x + 1 : super.plain(x) {...} // initialize _b, then call base class ctor, then run this body

ตัวสร้างที่เรียกใช้ตัวสร้างอื่นในคลาสเดียวกันใน Java และ C# อาจสร้างความสับสนได้เมื่อทั้งคู่มีการนำไปใช้งาน ใน Dart ข้อ จำกัด ที่การเปลี่ยนเส้นทางคอนสตรัคเตอร์ไม่สามารถมีเนื้อความได้บังคับให้โปรแกรมเมอร์ทำให้เลเยอร์ของคอนสตรัคเตอร์ชัดเจนขึ้น

นอกจากนี้ยังมีคีย์เวิร์ดของ factory ที่อนุญาตให้ใช้ฟังก์ชันได้เหมือนตัวสร้าง แต่การใช้งานเป็นเพียงฟังก์ชันปกติ คุณสามารถใช้เพื่อส่งคืนอินสแตนซ์แคชหรืออินสแตนซ์ของประเภทที่ได้รับ:

 class Shape { factory Shape(int nsides) { if (nsides == 4) return Square(); // etc. } } var s = Shape(4);

6. ตัวดัดแปลง

ใน Java และ C# เรามีตัวแก้ไขการเข้าถึง เช่น private , protected และ public ใน Dart สิ่งนี้ทำให้ง่ายขึ้นอย่างมาก: หากชื่อสมาชิกเริ่มต้นด้วยขีดล่าง จะเห็นได้ทุกที่ในแพ็คเกจ (รวมถึงจากคลาสอื่น) และซ่อนจากผู้โทรภายนอก มิฉะนั้นจะมองเห็นได้จากทุกที่ ไม่มีคีย์เวิร์ดที่เหมือนกับ private ที่บ่งบอกถึงการมองเห็น

ตัวดัดแปลงอีกประเภทหนึ่งควบคุมความสามารถในการเปลี่ยนแปลง: คำหลัก final และค่า const มีไว้เพื่อจุดประสงค์นั้น แต่มีความหมายต่างกัน:

 var a = 1; // a is variable, and can be reassigned later final b = a + 1; // b is a runtime constant, and can only be assigned once const c = 3; // c is a compile-time constant // const d = a + 2; // not allowed because a+2 cannot be resolved at compile time

7. ลำดับชั้นของคลาส

ภาษา Dart รองรับอินเตอร์เฟส คลาส และการสืบทอดหลายประเภท อย่างไรก็ตาม ไม่มีคำสำคัญเกี่ยวกับ interface แทน ทุกคลาสยังเป็นอินเตอร์เฟส ดังนั้นคุณสามารถกำหนดคลาส abstract แล้วนำไปใช้:

 abstract class HasDesk { bool isDeskMessy(); // no implementation here } class Employee implements HasDesk { bool isDeskMessy() { ...} // must be implemented here }

การสืบทอดหลายรายการเสร็จสิ้นด้วยสายเลือดหลักโดยใช้คีย์เวิร์ด extends และคลาสอื่นๆ โดยใช้คีย์เวิร์ด with :

 class Employee extends Person with Salaried implements HasDesk {...}

ในการประกาศนี้ คลาส Employee มาจาก Person และ Salaried แต่ Person เป็น superclass หลัก และ Salaried คือ mixin (superclass รอง)

8. ผู้ประกอบการ

มีโอเปอเรเตอร์ Dart ที่สนุกและมีประโยชน์ที่เราไม่คุ้นเคย

Cascades ช่วยให้คุณใช้รูปแบบการผูกมัดกับอะไรก็ได้:

 emp ..name = 'Alice' ..supervisor = 'Zoltron' ..hire();

ตัวดำเนินการสเปรดอนุญาตให้ดำเนินการกับคอลเล็กชันเสมือนเป็นรายการองค์ประกอบในเครื่องมือเริ่มต้น:

 var smallList = [1, 2]; var bigList = [0, ...smallList, 3, 4]; // [0, 1, 2, 3, 4]

9. หัวข้อ

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

พัฒนาด้วยภาษาปาเป้า: คุณทำได้!

หากคุณเป็นนักพัฒนา C# หรือ Java สิ่งที่คุณรู้อยู่แล้วจะช่วยให้คุณเรียนรู้ภาษา Dart ได้อย่างรวดเร็ว เนื่องจากถูกออกแบบให้คุ้นเคย ด้วยเหตุนี้ เราจึงได้รวบรวม PDF cheat sheet PDF สำหรับการอ้างอิงของคุณ โดยเน้นที่ความแตกต่างที่สำคัญจาก C# และ Java ที่เทียบเท่ากัน:

แผ่นโกงภาษาปาเป้า PDF

ความแตกต่างที่แสดงในบทความนี้รวมกับความรู้ที่มีอยู่จะช่วยให้คุณมีประสิทธิผลภายในวันแรกหรือสองวันแรกของ Dart มีความสุขในการเข้ารหัส!

ที่เกี่ยวข้อง: พลังไฮบริด: ข้อดีและประโยชน์ของการกระพือปีก