วิธีการปิดการรวบรวมแบบ Groovy การเปลี่ยนแปลงประเภทที่ไม่สอดคล้องกัน

Groovy มีพฤติกรรมแปลกๆ ดูสองตัวอย่างด้านล่าง:

def list = [[BigDecimal.ONE]]
list.each {
    println it.class
}

พิมพ์:

คลาส java.util.ArrayList

และ

def list = [[BigDecimal.ONE]]
list.each { BigDecimal it ->
    println it.class
}

พิมพ์:

คลาส java.math.BigDecimal

ความแตกต่างเพียงอย่างเดียวในตัวอย่างคืออันที่ 2 มีประเภทอาร์กิวเมนต์สำหรับการปิดที่ระบุ แต่นี่ไม่ได้อธิบายว่าทำไมและอย่างไร List ภายในจึงถูกแปลงเป็น BigDecimal ฉันคาดหวังไว้ดีกว่า ClassCastException นอกจากนี้พฤติกรรมนี้ไม่สอดคล้องกัน ราวกับว่ามีองค์ประกอบเพิ่มเติมในรายการภายในก็จะล้มเหลวด้วย MissingMethodException

เราพบว่าการแปลงประเภทเวทย์มนตร์นี้เกิดขึ้นใน ClosureMetaClass (บรรทัด: 256)

มันเป็นพฤติกรรมที่ออกแบบมาหรือเป็นข้อบกพร่อง?

แก้ไข: ฉันพบปัญหาข้างต้นขณะพยายามขัดขวางวิธีการด้วย Spock วิธีการรับ Collection เป็นพารามิเตอร์ ลองพิจารณาอีกตัวอย่างหนึ่ง:

def 'stub a method with collection as argument'() {
    given:
    def input = [1, 2, 3]
    def capturedArgument
    List listStub = Stub()
    listStub.addAll(input) >> {
        capturedArgument = it
    }

    when:
    listStub.addAll(input)

    then:
    input.class == capturedArgument.class
}

มันล้มเหลวด้วย:

Condition not satisfied:

input.class == capturedArgument.class
|     |     |  |                |
|     |     |  [[1, 2, 3]]      class java.util.Arrays$ArrayList
|     |     false
|     class java.util.ArrayList
[1, 2, 3]

ปัญหาคืออาร์กิวเมนต์ it มาเป็น List ฝังอยู่ในอีก List ลงในการปิดเมธอด stubbing อะไรนะ?

วิธีเดียวที่จะเอาชนะสิ่งนี้ได้คือวิธีการขัดจังหวะด้วย ประเภทอาร์กิวเมนต์ที่เหมือนกันทุกประการกับประเภทอินพุต เช่น

listStub.addAll(input) >> { ArrayList it ->

...จากนั้นก็สอบผ่าน เป็นไปไม่ได้เลยเพราะฉันต้องใช้อินเทอร์เฟซเป็นประเภทอาร์กิวเมนต์ stub ไม่ใช่การใช้งานเฉพาะ และเมื่อมีการประกาศเช่นนั้น

listStub.addAll(input) >> { List it ->

or

listStub.addAll(input) >> { Collection it ->

...มันล้มเหลวในลักษณะเดียวกับที่ไม่มีประเภทเพราะรายการ input ถูกฝังอยู่ในรายการอื่น

นี่คือตัวอย่างสด หากคุณต้องการวิ่งและเล่นกับมัน


person topr    schedule 05.01.2015    source แหล่งที่มา
comment
ประเภทของ captureArguments คือ: java.util.Arrays$ArrayList เนื่องจากโค้ดส่วนนี้ใน DefaultGroovyMethods.java: public static <T> boolean addAll(Collection<T> self, T[] items) { return self.addAll(Arrays.asList(items)); } มันทำงานถูกต้องทั้งหมด หากคุณระบุอาร์กิวเมนต์อย่างชัดเจน อาร์กิวเมนต์นั้นจะถูกแปลงโดยอัตโนมัติและจะไม่มีข้อยกเว้นเกิดขึ้น ดู: github com/groovy/groovy-core/blob/master/src/main/org/codehaus/   -  person Opal    schedule 05.01.2015
comment
ในการตอบกลับจำลอง Spock it หรือพารามิเตอร์การปิดที่ไม่ได้พิมพ์เพียงรายการเดียวจะมีรายการอาร์กิวเมนต์ของเมธอด หากใช้พารามิเตอร์การปิดที่พิมพ์แทน Spock จะพยายามทำลายโครงสร้างตามความหมายของ Groovy ดูที่ docs.spockframework.org   -  person Peter Niederwieser    schedule 05.01.2015
comment
ขอบคุณปีเตอร์ ตอนนี้ฉันเข้าใจแล้วว่าทำไมรายการอินพุตของฉันจึงถูกฝังอยู่ในรายการอื่นสำหรับอาร์กิวเมนต์การตอบสนองการเยาะเย้ย Spock ที่ไม่ได้พิมพ์ แต่เหตุใดสิ่งเดียวกันนี้จึงเกิดขึ้นแม้ว่าจะมีการพิมพ์อาร์กิวเมนต์ แต่มีคลาสพาเรนต์หรืออินเทอร์เฟซ ฉันจะดูในเอกสารอีกครั้งแน่นอน แต่ถ้าคุณบอกอะไรเพิ่มเติมเกี่ยวกับเรื่องนั้นได้บ้างจะดีมาก นี่คือตัวอย่างสิ่งที่ฉันถามเกี่ยวกับ meetspock.appspot.com/script/5723151296102400 ด้วยวิธีนี้ จำเป็นต้องขัดวิธีเดียวกันหลายครั้งเพื่อพูดว่า: LinkedList, ArrayList, Set, ฯลฯ ... มันดูไม่ค่อยดีนัก แม้แต่ CodeNarc ก็บ่นว่า ImplementationAsType   -  person topr    schedule 05.01.2015
comment
การทำลายล้างจะ (เท่านั้น) เริ่มทำงานหากไม่สามารถส่งอาร์กิวเมนต์การปิดตามที่เป็นอยู่ตามประเภทพารามิเตอร์ที่ประกาศของการปิด (ตรงกันข้ามกับสิ่งที่ฉันได้พูดไปก่อนหน้านี้ ทั้งหมด ทั้งหมดนี้อยู่ภายใต้การควบคุมของ Groovy และดังนั้นจึงเป็นไปตามความหมายของ Groovy มาตรฐาน สป็อคเพียงแค่เรียกการปิดด้วยอาร์กิวเมนต์ประเภทเดียวประเภท List ซึ่งแสดงถึงอาร์กิวเมนต์ของการร้องขอที่ถูกตัดทอน list.) กล่าวอีกนัยหนึ่ง เมื่อทำการสแตกเมธอดด้วยพารามิเตอร์ประเภทเดียว Iterable, Collection หรือ List คุณจะต้องทำลายโครงสร้างด้วยตนเอง (>> { args -> args[0] } หรือ >> { it[0] })   -  person Peter Niederwieser    schedule 06.01.2015
comment
^^^ ประโยคสุดท้ายนี้คงจะดีถ้ามีในเอกสารประกอบ   -  person Snekse    schedule 05.04.2016


คำตอบ (1)


Groovy จะทำลายโครงสร้างรายการที่จัดเตรียมไว้เพื่อการปิด (ตัวอย่างที่ดีที่สุดคือแต่ละรายการใน Map โดยที่คีย์และค่าถูกส่งผ่าน) จึงมีความสม่ำเสมอในการใช้งานสม่ำเสมอ:

[[BigDecimal.ONE],[BigDecimal.ONE]].each{ BigDecimal it -> println it } 
//=> 1
//=> 1
[[BigDecimal.ONE, BigDecimal.ONE]].each{ a, b -> println "$a and $b" }
//=> 1 and 1
person cfrick    schedule 05.01.2015
comment
ขอบคุณสำหรับคำอธิบายนี้ ฉันสามารถเห็นสาเหตุของพฤติกรรมดังกล่าวด้วย Map และฉันก็ใช้มันค่อนข้างบ่อย อย่างไรก็ตาม ด้วย Collection อาจนำไปสู่พฤติกรรมที่ไม่คาดคิดของแอป อย่างไรก็ตาม คุณช่วยดูในส่วน 'แก้ไข' ในคำถามเดิมของฉันและอธิบายปริศนานี้ได้ไหม - person topr; 05.01.2015
comment
ขออภัย ฉันไม่สามารถช่วยแก้ปัญหาสป็อคได้ และนี่จะเป็นการตั้งคำถามใหม่ด้วยซ้ำ (เช่น ปีเตอร์จะมีโอกาสที่จะให้คำตอบที่ชัดเจน) - person cfrick; 06.01.2015
comment
ขอบคุณครับ ไม่แน่ใจเหมือนกันหรือเปล่าครับ จริงๆแล้วมันไม่เหมือนกันแต่เกี่ยวข้องกัน ฉันกำลังพิจารณาที่จะถามคำถามนี้เป็นคำถามใหม่และบางทีฉันควรจะถาม - person topr; 07.01.2015