Groovy: อะไรคือจุดประสงค์ของ“ def” ใน“ def x = 0”?


180

ในชิ้นต่อไปนี้รหัส (นำมาจากGroovy อรรถหน้าคู่มือ ) ทำไมคำนำหน้ามอบหมายด้วยคำหลักdef?

def x = 0
def y = 5

while ( y-- > 0 ) {
    println "" + x + " " + y
    x++
}

assert x == 5

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

คำตอบ:


278

มันเป็นน้ำตาลประโยคสำหรับสคริปต์พื้นฐาน การละเว้นคำหลัก "def" จะทำให้ตัวแปรอยู่ในการเชื่อมโยงสำหรับสคริปต์ปัจจุบันและ groovy ปฏิบัติต่อมัน (ส่วนใหญ่) เหมือนกับตัวแปรที่กำหนดขอบเขตทั่วโลก:

x = 1
assert x == 1
assert this.binding.getVariable("x") == 1

การใช้คำหลัก def แทนไม่ได้ใส่ตัวแปรในการผูกสคริปต์:

def y = 2

assert y == 2

try {
    this.binding.getVariable("y") 
} catch (groovy.lang.MissingPropertyException e) {
    println "error caught"
} 

พิมพ์: "จับข้อผิดพลาด"

การใช้คีย์เวิร์ด def ในโปรแกรมที่มีขนาดใหญ่นั้นมีความสำคัญเนื่องจากช่วยกำหนดขอบเขตที่ตัวแปรสามารถพบได้และสามารถช่วยรักษา encapsulation ได้

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

 x = 1
 def y = 2


public bar() {
    assert x == 1

    try {
        assert y == 2
    } catch (groovy.lang.MissingPropertyException e) {
        println "error caught"
    }
}

bar()

พิมพ์ "จับข้อผิดพลาด"

ตัวแปร "y" ไม่ได้อยู่ในขอบเขตภายในฟังก์ชั่น "x" อยู่ในขอบเขตที่ Groovy จะตรวจสอบการเชื่อมโยงของสคริปต์ปัจจุบันสำหรับตัวแปร ดังที่ฉันได้กล่าวไว้ก่อนหน้านี้เป็นเพียงน้ำตาลประโยคเพื่อให้สคริปต์ที่รวดเร็วและสกปรกสามารถพิมพ์ออกมาได้อย่างรวดเร็ว

แนวปฏิบัติที่ดีในสคริปต์ที่ใหญ่กว่าคือการใช้คำหลัก "def" เสมอดังนั้นคุณจะไม่พบปัญหาการกำหนดขอบเขตแปลก ๆ หรือรบกวนตัวแปรที่คุณไม่ต้องการ


36

คำตอบของ Tedนั้นยอดเยี่ยมสำหรับสคริปต์ คำตอบของเบ็นเป็นมาตรฐานสำหรับชั้นเรียน

อย่างที่เบ็นบอกให้คิดว่ามันเป็น "วัตถุ" - แต่มันก็เจ๋งกว่ามากในการที่มันไม่ได้ จำกัด คุณกับวิธีการของวัตถุ สิ่งนี้มีความหมายตามระเบียบที่เกี่ยวกับการนำเข้า

เช่นในตัวอย่างนี้ฉันต้องนำเข้า FileChannel

// Groovy imports java.io.* and java.util.* automatically
// but not java.nio.*

import java.nio.channels.*

class Foo {
    public void bar() {
        FileChannel channel = new FileInputStream('Test.groovy').getChannel()
        println channel.toString()
    }
}

new Foo().bar()

เช่น แต่ที่นี่ฉันสามารถ 'เปิดปีก' ตราบใดที่ทุกอย่างอยู่ใน classpath

// Groovy imports java.io.* and java.util.* automatically
// but not java.nio.*
class Foo {
    public void bar() {
        def channel = new FileInputStream('Test.groovy').getChannel()
        println channel.toString()
    }
}

new Foo().bar()

1
ทำไมคุณถึงไม่ได้รับอนุญาตให้new FileInputStream('Test.groovy').getChannel()นำเข้า?
Alexander Suraphel

3
@AlexanderSuraphel "ตราบใดที่ทุกอย่างอยู่บนคลาสพา ธ"
ฮันโน

30

ตามนี้หน้า , defเป็นแทนสำหรับชื่อประเภทและสามารถก็จะคิดว่าเป็นนามแฝงสำหรับObject(เช่นแสดงว่าคุณไม่สนใจเกี่ยวกับประเภท)


12

เท่าที่สคริปต์นี้เกี่ยวข้องไม่มีความแตกต่างในทางปฏิบัติ

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

ดังนั้นหากคุณมีสองสคริปต์และดำเนินการด้วย GroovyShell เดียวกันสคริปต์ที่สองจะสามารถรับตัวแปรทั้งหมดที่ตั้งค่าไว้ในสคริปต์แรกโดยไม่มี "def"


8

เหตุผลของการ "def" คือการบอก groovy ว่าคุณตั้งใจจะสร้างตัวแปรที่นี่ มันสำคัญเพราะคุณไม่ต้องการสร้างตัวแปรโดยไม่ตั้งใจ

มันค่อนข้างยอมรับได้ในสคริปต์ (สคริปต์ Groovy และ groovysh อนุญาตให้คุณทำเช่นนั้น) แต่ในรหัสการผลิตเป็นหนึ่งในความชั่วร้ายที่ยิ่งใหญ่ที่สุดที่คุณสามารถเจอได้ซึ่งเป็นสาเหตุที่คุณต้องกำหนดตัวแปรด้วย def ในรหัส groovy จริงทั้งหมด class)

นี่คือตัวอย่างของสาเหตุที่ไม่ดี สิ่งนี้จะทำงาน (โดยไม่ล้มเหลวในการยืนยัน) หากคุณคัดลอกรหัสต่อไปนี้และวางลงใน groovysh:

bill = 7
bi1l = bill + 3
assert bill == 7

ปัญหาแบบนี้อาจต้องใช้เวลานานในการค้นหาและแก้ไข - แม้ว่ามันจะกัดคุณเพียงครั้งเดียวในชีวิต แต่ก็ยังต้องเสียเวลามากกว่าการประกาศตัวแปรหลายพันครั้งตลอดอาชีพของคุณอย่างชัดเจน นอกจากนี้ยังชัดเจนต่อตาเมื่อมีการประกาศคุณไม่ต้องเดา

ในอินพุตสคริปต์ / คอนโซลที่ไม่สำคัญ (เช่นคอนโซล groovy) จะค่อนข้างยอมรับได้เนื่องจากขอบเขตของสคริปต์มี จำกัด ฉันคิดว่าเหตุผลเดียวที่ groovy อนุญาตให้คุณทำสิ่งนี้ในสคริปต์คือการสนับสนุน DSL ในแบบที่ Ruby ทำ (การแลกเปลี่ยนที่ไม่ดีถ้าคุณถามฉัน แต่บางคนรัก DSL)


5

จริงๆแล้วฉันไม่คิดว่ามันจะทำงานเหมือนเดิม ...

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

เมื่อฉันพยายามใช้ตัวแปรที่ฉันไม่ได้ประกาศด้วย def หรือประเภทฉันได้รับข้อผิดพลาด "ไม่มีคุณสมบัติดังกล่าว" เนื่องจากมันถือว่าฉันใช้สมาชิกของคลาสที่มีรหัส

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