วิธีการส่งผ่าน Java เป็นพารามิเตอร์


277

ฉันกำลังมองหาวิธีที่จะผ่านวิธีการโดยอ้างอิง ฉันเข้าใจว่า Java ไม่ผ่านวิธีการเป็นพารามิเตอร์อย่างไรก็ตามฉันต้องการได้รับทางเลือก

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

สิ่งที่ฉันต้องการทำให้สำเร็จคือสิ่งที่คล้ายกับสิ่งนี้:

public void setAllComponents(Component[] myComponentArray, Method myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { //recursive call if Container
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } //end if node
        myMethod(leaf);
    } //end looping through components
}

เรียกใช้เช่น:

setAllComponents(this.getComponents(), changeColor());
setAllComponents(this.getComponents(), changeSize());

ตอนนี้ทางออกของฉันคือการส่งพารามิเตอร์เพิ่มเติมและใช้ตัวเรือนสวิทช์ด้านในเพื่อเลือกวิธีการที่เหมาะสม อย่างไรก็ตามโซลูชันนี้ไม่ให้ยืมในขณะที่ใช้รหัสซ้ำ

ดูคำตอบนี้stackoverflow.com/a/22933032/1010868สำหรับคำถามที่คล้ายกัน
Tomasz Gawel

คำตอบ:


233

แก้ไข : ตั้งแต่ Java 8 การแสดงออกแลมบ์ดาเป็นทางออกที่ดีเพราะคำตอบอื่น ๆ ได้ชี้ให้เห็น คำตอบด้านล่างนี้เขียนขึ้นสำหรับ Java 7 และรุ่นก่อนหน้า ...


ลองดูที่เป็นรูปแบบคำสั่ง

// NOTE: code not tested, but I believe this is valid java...
public class CommandExample 
{
    public interface Command 
    {
        public void execute(Object data);
    }

    public class PrintCommand implements Command 
    {
        public void execute(Object data) 
        {
            System.out.println(data.toString());
        }    
    }

    public static void callCommand(Command command, Object data) 
    {
        command.execute(data);
    }

    public static void main(String... args) 
    {
        callCommand(new PrintCommand(), "hello world");
    }
}

แก้ไข:เป็นพีทริ์กชี้ให้เห็นว่ามีวิธีการทำเช่นนี้โดยใช้อีกจำนวนผู้เข้าชม วิธีการของผู้เยี่ยมชมนั้นมีส่วนเกี่ยวข้องมากกว่า - โหนดของคุณทุกคนจำเป็นต้องรู้acceptVisitor()วิธีการของผู้เข้าชม- แต่ถ้าคุณต้องการสำรวจกราฟวัตถุที่ซับซ้อนมากขึ้น


2
@Mac - ดีมาก! อันนี้เกิดขึ้นซ้ำแล้วซ้ำอีกในภาษาที่ไม่มีวิธีการชั้นหนึ่งเป็นวิธีการเลียนแบบพวกเขาดังนั้นจึงเป็นที่น่าจดจำ
Dan Vinton

7
เป็นรูปแบบผู้เยี่ยมชม (แยกการกระทำของการวนซ้ำในคอลเลกชันจากฟังก์ชันที่ใช้กับสมาชิกแต่ละคนของคอลเลกชัน) ไม่ใช่รูปแบบคำสั่ง คุณไม่ได้ใส่แค็ปซูลอาร์กิวเมนต์โดยเฉพาะซึ่งเป็นส่วนที่ทำซ้ำของรูปแบบผู้เข้าชม
Pete Kirkham

ไม่คุณต้องใช้วิธีการยอมรับเฉพาะเมื่อคุณรวมการเยี่ยมชมด้วยการส่งแบบคู่ หากคุณมีผู้เยี่ยมชม monomorphic ก็เป็นรหัสที่คุณมีข้างต้น
Pete Kirkham

ใน Java 8 อาจเป็น ex.operS (String :: toLowerCase, "STRING") ดูบทความที่ดี: studytrails.com/java/java8/…
Zon

Pete Kirkham ถูกต้อง: รหัสของคุณกำลังใช้รูปแบบผู้เยี่ยมชมไม่ใช่รูปแบบคำสั่ง (และนี่เป็นสิ่งที่ดีเนื่องจากเป็นสิ่งที่ OP ต้องการ) ดังที่ Pete กล่าวว่าคุณไม่ได้รวมการโต้แย้งดังนั้นคุณจึงไม่ได้ทำ Command - Command อินเตอร์เฟสมีตัวประมวลผลที่รับพารามิเตอร์ Wikipedia ไม่ได้ นี่คือพื้นฐานของเจตนาของรูปแบบคำสั่ง ตามที่ย่อหน้าแรกบอกว่า "สรุปข้อมูลทั้งหมด ... ข้อมูลนี้รวมถึงชื่อเมธอดวัตถุที่เป็นเจ้าของเมธอดและค่าสำหรับพารามิเตอร์เมธอด "
ToolmakerSteve

73

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

โดยไม่ต้องแสดงออกแลมบ์ดา:

obj.aMethod(new AFunctionalInterface() {
    @Override
    public boolean anotherMethod(int i)
    {
        return i == 982
    }
});

ด้วยการแสดงออกแลมบ์ดา:

obj.aMethod(i -> i == 982);

นี่คือข้อความที่ตัดตอนมาจากบทช่วยสอน Java บน Lambda Expressions :

ไวยากรณ์ของแลมบ์ดานิพจน์

การแสดงออกแลมบ์ดาประกอบด้วยดังต่อไปนี้:

  • รายการที่คั่นด้วยเครื่องหมายจุลภาคของพารามิเตอร์ที่เป็นทางการซึ่งอยู่ในวงเล็บ กระบวนการ CheckPerson.test ประกอบด้วยหนึ่งพารามิเตอร์ p ซึ่งแสดงถึงอินสแตนซ์ของคลาสบุคคล

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

    p -> p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25
  • โทเค็นลูกศร ->

  • เนื้อความซึ่งประกอบด้วยนิพจน์เดียวหรือบล็อกคำสั่ง ตัวอย่างนี้ใช้นิพจน์ต่อไปนี้:

    p.getGender() == Person.Sex.MALE 
        && p.getAge() >= 18
        && p.getAge() <= 25

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

    p -> {
        return p.getGender() == Person.Sex.MALE
            && p.getAge() >= 18
            && p.getAge() <= 25;
    }

    ข้อความสั่ง return ไม่ใช่นิพจน์ ในนิพจน์แลมบ์ดาคุณต้องใส่คำสั่งเป็นวงเล็บปีกกา ({}) อย่างไรก็ตามคุณไม่จำเป็นต้องล้อมรอบการเรียกใช้โมฆะวิธีในวงเล็บปีกกา ตัวอย่างเช่นต่อไปนี้เป็นนิพจน์แลมบ์ดาที่ถูกต้อง:

    email -> System.out.println(email)

โปรดทราบว่าการแสดงออกแลมบ์ดามีลักษณะเหมือนการประกาศวิธีการมาก คุณสามารถพิจารณาแลมบ์ดานิพจน์เป็นวิธีที่ไม่ระบุชื่อ - วิธีการที่ไม่มีชื่อ


นี่คือวิธีที่คุณสามารถ "ผ่านวิธีการ" โดยใช้การแสดงออกแลมบ์ดา:

interface I {
    public void myMethod(Component component);
}

class A {
    public void changeColor(Component component) {
        // code here
    }

    public void changeSize(Component component) {
        // code here
    }
}
class B {
    public void setAllComponents(Component[] myComponentArray, I myMethodsInterface) {
        for(Component leaf : myComponentArray) {
            if(leaf instanceof Container) { // recursive call if Container
                Container node = (Container)leaf;
                setAllComponents(node.getComponents(), myMethodInterface);
            } // end if node
            myMethodsInterface.myMethod(leaf);
        } // end looping through components
    }
}
class C {
    A a = new A();
    B b = new B();

    public C() {
        b.setAllComponents(this.getComponents(), component -> a.changeColor(component));
        b.setAllComponents(this.getComponents(), component -> a.changeSize(component));
    }
}

คลาสCสามารถย่อให้สั้นลงอีกเล็กน้อยโดยใช้การอ้างอิงเมธอดดังนี้:

class C {
    A a = new A();
    B b = new B();

    public C() {
        b.setAllComponents(this.getComponents(), a::changeColor);
        b.setAllComponents(this.getComponents(), a::changeSize);
    }
}

Class A จำเป็นต้องได้รับการสืบทอดจากอินเตอร์เฟสหรือไม่?
Serob_b

1
@Serob_b ไม่ ถ้าคุณไม่ต้องการผ่านมันเป็นการอ้างอิงวิธีการ (ดู::โอเปอเรเตอร์) มันไม่สำคัญว่า A คืออะไร a.changeThing(component)สามารถเปลี่ยนเป็นคำสั่งหรือบล็อครหัสที่คุณต้องการตราบใดที่มันคืนค่าเป็นโมฆะ
ผู้ชายกับหมวก

29

ใช้java.lang.reflect.Methodวัตถุและการโทรinvoke


12
ฉันไม่เห็นว่าทำไม คำถามคือการส่งเมธอดเป็นพารามิเตอร์และนี่เป็นวิธีที่ถูกต้องมากในการทำมัน นอกจากนี้ยังสามารถห่อในรูปแบบสวย ๆ จำนวนมากเพื่อให้ดูดี และนี่เป็นเรื่องทั่วไปตามที่ได้รับโดยไม่จำเป็นต้องมีอินเตอร์เฟสพิเศษใด ๆ
Vinodh Ramasubramanian

3
คุณพิมพ์ความปลอดภัยใน JavaScript fg หรือไม่ ความปลอดภัยของประเภทไม่ใช่การโต้แย้ง
Danubian Sailor

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

21
"สิ่งอำนวยความสะดวกการสะท้อนกลับหลักได้รับการออกแบบมาสำหรับเครื่องมือสร้างแอพพลิเคชั่นที่อิงส่วนประกอบ [... ] ตามกฎแล้ววัตถุไม่ควรเข้าถึงได้อย่างชัดเจนในแอปพลิเคชันปกติที่รันไทม์" รายการที่ 53: ต้องการส่วนต่อประสานกับการสะท้อนจาก Effective Java Second Edition - นั่นคือความคิดของผู้สร้าง Java ;-)
Wilhem Meignan

8
ไม่ใช่การใช้การสะท้อนที่สมเหตุสมผล ฉันกลัวที่จะเห็น upvotes ทั้งหมด ไตร่ตรองไม่เคยตั้งใจจะใช้เป็นกลไกการเขียนโปรแกรมทั่วไป ใช้งานได้ก็ต่อเมื่อไม่มีวิธีแก้ปัญหาอื่น ๆ ที่สะอาด
ToolmakerSteve

22

ตั้งแต่ Java 8 มีFunction<T, R>อินเตอร์เฟส ( เอกสาร ) ซึ่งมีเมธอด

R apply(T t);

คุณสามารถใช้มันเพื่อส่งผ่านฟังก์ชั่นเป็นพารามิเตอร์ไปยังฟังก์ชั่นอื่น ๆ T คือประเภทอินพุตของฟังก์ชั่น R คือประเภทย้อนกลับ

ในตัวอย่างของคุณคุณต้องผ่านฟังก์ชั่นที่ใช้Componentประเภทเป็น input และผลตอบแทนอะไร Void- ในกรณีFunction<T, R>นี้ไม่ใช่ตัวเลือกที่ดีที่สุดเนื่องจากไม่มีการล็อคอัตโนมัติประเภท Void อินเทอร์เฟซที่คุณกำลังค้นหาเรียกว่าConsumer<T>( เอกสาร ) ด้วยวิธีการ

void accept(T t);

มันจะมีลักษณะเช่นนี้:

public void setAllComponents(Component[] myComponentArray, Consumer<Component> myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { 
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } 
        myMethod.accept(leaf);
    } 
}

และคุณจะเรียกมันว่าใช้วิธีการอ้างอิง:

setAllComponents(this.getComponents(), this::changeColor);
setAllComponents(this.getComponents(), this::changeSize); 

สมมติว่าคุณได้กำหนดเมธอด changeColor () และ changeSize () ในคลาสเดียวกัน


หากวิธีการของคุณยอมรับมากกว่าหนึ่งพารามิเตอร์คุณสามารถใช้BiFunction<T, U, R>- T และ U เป็นประเภทของพารามิเตอร์อินพุตและ R เป็นประเภทส่งคืน นอกจากนี้ยังมีBiConsumer<T, U>(สองข้อโต้แย้งไม่มีประเภทผลตอบแทน) น่าเสียดายสำหรับพารามิเตอร์อินพุต 3 พารามิเตอร์ขึ้นไปคุณต้องสร้างส่วนต่อประสานด้วยตัวเอง ตัวอย่างเช่น:

public interface Function4<A, B, C, D, R> {

    R apply(A a, B b, C c, D d);
}

19

ก่อนกำหนดอินเทอร์เฟซด้วยวิธีการที่คุณต้องการส่งผ่านเป็นพารามิเตอร์

public interface Callable {
  public void call(int param);
}

ใช้คลาสด้วยวิธีการ

class Test implements Callable {
  public void call(int param) {
    System.out.println( param );
  }
}

// วิงวอนอย่างนั้น

Callable cmd = new Test();

สิ่งนี้อนุญาตให้คุณส่ง cmd เป็นพารามิเตอร์และเรียกใช้เมธอดที่กำหนดในอินเตอร์เฟส

public invoke( Callable callable ) {
  callable.call( 5 );
}

1
คุณอาจไม่จำเป็นต้องสร้างอินเทอร์เฟซของตัวเองเนื่องจากจาวาได้กำหนดจำนวนมากให้กับคุณแล้ว: docs.oracle.com/javase/8/docs/api/java/util/function/ ......
slim

@slim จุดสนใจคำจำกัดความเหล่านั้นมีความเสถียรเพียงใดพวกเขาตั้งใจจะใช้ตามปรกติตามที่คุณแนะนำหรือมีแนวโน้มที่จะแตก?
มานูเอล

@slim ที่จริงแล้วเอกสารตอบว่า: "อินเทอร์เฟซในแพคเกจนี้เป็นอินเทอร์เฟซที่ใช้งานได้อเนกประสงค์ที่ใช้โดย JDK และพร้อมใช้งานโดยรหัสผู้ใช้ด้วย"
มานูเอล

14

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

กล่าวคือเวอร์ชันใหม่นี้นำlambdasและวิธีการอ้างอิงไปยัง Java (พร้อมกับAPI ใหม่ซึ่งเป็นวิธีแก้ไขปัญหาที่ถูกต้องสำหรับปัญหานี้ในขณะที่พวกเขายังต้องการอินเทอร์เฟซที่ไม่มีการสร้างวัตถุใหม่และ classfiles พิเศษไม่ต้องการไดเรกทอรีไดเรกทอรีมลพิษ จัดการโดย JVM

ทั้งรสชาติ (แลมบ์ดาและการอ้างอิงเมธอด) ต้องการอินเทอร์เฟซที่ใช้งานได้กับวิธีการเดียวที่ใช้ลายเซ็น:

public interface NewVersionTest{
    String returnAString(Object oIn, String str);
}

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

public static void printOutput(NewVersionTest t, Object o, String s){
    System.out.println(t.returnAString(o, s));
}

นี่เป็นเพียงการเรียกใช้อินเทอร์เฟซที่เรียบง่ายจนกระทั่งแลมบ์ดา1ได้รับการส่งผ่าน:

public static void main(String[] args){
    printOutput( (Object oIn, String sIn) -> {
        System.out.println("Lambda reached!");
        return "lambda return";
    }
    );
}

สิ่งนี้จะออก:

Lambda reached!
lambda return

วิธีการอ้างอิงที่คล้ายกัน ได้รับ:

public class HelperClass{
    public static String testOtherSig(Object o, String s){
        return "real static method";
    }
}

และหลัก:

public static void main(String[] args){
    printOutput(HelperClass::testOtherSig);
}

real static methodการส่งออกจะเป็น อ้างอิงวิธีที่จะสามารถคงที่เช่นไม่คงที่กับกรณีพลและแม้กระทั่งการก่อสร้าง สำหรับตัวสร้างบางสิ่งบางอย่างคล้ายกับที่ClassName::newจะใช้

1สิ่งนี้ไม่ถือเป็นแลมบ์ดาเนื่องจากมีผลข้างเคียง มันแสดงให้เห็นถึงการใช้งานของหนึ่งในแฟชั่นตรงไปตรงมามากขึ้น


12

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

public interface ComponentMethod {
  public abstract void PerfromMethod(Container c);
}

public class ChangeColor implements ComponentMethod {
  @Override
  public void PerfromMethod(Container c) {
    // do color change stuff
  }
}

public class ChangeSize implements ComponentMethod {
  @Override
  public void PerfromMethod(Container c) {
    // do color change stuff
  }
}

public void setAllComponents(Component[] myComponentArray, ComponentMethod myMethod) {
    for (Component leaf : myComponentArray) {
        if (leaf instanceof Container) { //recursive call if Container
            Container node = (Container) leaf;
            setAllComponents(node.getComponents(), myMethod);
        } //end if node
        myMethod.PerfromMethod(leaf);
    } //end looping through components
}

สิ่งที่คุณต้องการเรียกใช้ด้วย:

setAllComponents(this.getComponents(), new ChangeColor());
setAllComponents(this.getComponents(), new ChangeSize());

6

หากคุณไม่ต้องการวิธีการเหล่านี้เพื่อส่งคืนบางสิ่งคุณสามารถทำให้พวกเขาคืนค่าวัตถุที่เรียกใช้

private Runnable methodName (final int arg) {
    return (new Runnable() {
        public void run() {
          // do stuff with arg
        }
    });
}

จากนั้นใช้มันเหมือน:

private void otherMethodName (Runnable arg){
    arg.run();
}

2

ฉันไม่พบตัวอย่างชัดเจนเพียงพอสำหรับฉันเกี่ยวกับวิธีการใช้java.util.function.Functionสำหรับวิธีการง่าย ๆ เป็นฟังก์ชันพารามิเตอร์ นี่คือตัวอย่างง่ายๆ:

import java.util.function.Function;

public class Foo {

  private Foo(String parameter) {
    System.out.println("I'm a Foo " + parameter);
  }

  public static Foo method(final String parameter) {
    return new Foo(parameter);
  }

  private static Function parametrisedMethod(Function<String, Foo> function) {
    return function;
  }

  public static void main(String[] args) {
    parametrisedMethod(Foo::method).apply("from a method");
  }
}

โดยทั่วไปคุณมีFooวัตถุที่มีตัวสร้างเริ่มต้น methodที่จะถูกเรียกว่าเป็นพารามิเตอร์จากที่ซึ่งเป็นประเภทparametrisedMethodFunction<String, Foo>

  • Function<String, Foo>หมายความว่าฟังก์ชั่นใช้เวลาเป็นพารามิเตอร์และผลตอบแทนStringFoo
  • Foo::Methodสอดคล้องกับแลมบ์ดาเหมือนx -> Foo.method(x);
  • parametrisedMethod(Foo::method) อาจถูกมองว่าเป็น x -> parametrisedMethod(Foo.method(x))
  • .apply("from a method")เป็นพื้นจะทำอย่างไรparametrisedMethod(Foo.method("from a method"))

ซึ่งจะส่งกลับในผลลัพธ์:

>> I'm a Foo from a method

ตัวอย่างควรจะทำงานตามที่เป็นอยู่จากนั้นคุณสามารถลองสิ่งที่ซับซ้อนมากขึ้นจากคำตอบข้างต้นกับชั้นเรียนและอินเตอร์เฟซที่แตกต่างกัน


ในการใช้การโทรสมัครบน Android คุณต้องมี API ขั้นต่ำ 24
Ines Belhouchet

1

Java มีกลไกในการส่งชื่อและเรียกมันว่า มันเป็นส่วนหนึ่งของกลไกการสะท้อน ฟังก์ชั่นของคุณควรใช้พารามิเตอร์เพิ่มเติมของวิธีการเรียน

public void YouMethod(..... Method methodToCall, Object objWithAllMethodsToBeCalled)
{
...
Object retobj = methodToCall.invoke(objWithAllMethodsToBeCalled, arglist);
...
}

1

ฉันไม่ใช่ผู้เชี่ยวชาญเกี่ยวกับจาวา แต่ฉันแก้ปัญหาของคุณเช่นนี้:

@FunctionalInterface
public interface AutoCompleteCallable<T> {
  String call(T model) throws Exception;
}

ฉันกำหนดพารามิเตอร์ในส่วนต่อประสานพิเศษของฉัน

public <T> void initialize(List<T> entries, AutoCompleteCallable getSearchText) {.......
//call here
String value = getSearchText.call(item);
...
}

สุดท้ายฉันใช้วิธีgetSearchTextในขณะที่เรียกวิธีการเตรียมใช้งาน

initialize(getMessageContactModelList(), new AutoCompleteCallable() {
          @Override
          public String call(Object model) throws Exception {
            return "custom string" + ((xxxModel)model.getTitle());
          }
        })

จริงๆแล้วมันเป็นคำตอบที่ดีที่สุดและวิธีการที่เหมาะสมที่จะทำ ได้รับมากกว่า +1
amdev

0

ใช้รูปแบบการสังเกตการณ์ (บางครั้งเรียกว่ารูปแบบการฟัง):

interface ComponentDelegate {
    void doSomething(Component component);
}

public void setAllComponents(Component[] myComponentArray, ComponentDelegate delegate) {
    // ...
    delegate.doSomething(leaf);
}

setAllComponents(this.getComponents(), new ComponentDelegate() {
                                            void doSomething(Component component) {
                                                changeColor(component); // or do directly what you want
                                            }
                                       });

new ComponentDelegate()... ประกาศประเภทไม่ระบุชื่อการใช้อินเตอร์เฟซ


8
นี่ไม่ใช่รูปแบบที่คุณต้องการ
Pete Kirkham

1
รูปแบบของผู้สังเกตการณ์นั้นเกี่ยวกับการย่อความสามารถในการตอบสนองต่อการเปลี่ยนแปลง OP ต้องการสรุปการกระทำที่เกิดขึ้นในแต่ละรายการในคอลเล็กชันห่างจากรหัสที่วนซ้ำในคอลเลกชันซึ่งเป็นรูปแบบของผู้เข้าชม
Pete Kirkham

รูปแบบของ Observer / Listener นั้นเหมือนกันกับรูปแบบคำสั่งนั้น พวกเขามีความตั้งใจแตกต่างกันเท่านั้น ผู้สังเกตการณ์เกี่ยวกับการแจ้งเตือนในขณะที่คำสั่งใช้แทนฟังก์ชัน / lambdas ชั้นหนึ่ง ผู้เข้าชมในทางตรงกันข้ามเป็นสิ่งที่แตกต่างอย่างสิ้นเชิง ฉันไม่คิดว่ามันสามารถอธิบายได้ในสองประโยคดังนั้นโปรดดูที่en.wikipedia.org/wiki/Visitor_pattern
EricSchaefer

0

นี่คือตัวอย่างพื้นฐาน:

public class TestMethodPassing
{
    private static void println()
    {
        System.out.println("Do println");
    }

    private static void print()
    {
        System.out.print("Do print");
    }

    private static void performTask(BasicFunctionalInterface functionalInterface)
    {
        functionalInterface.performTask();
    }

    @FunctionalInterface
    interface BasicFunctionalInterface
    {
        void performTask();
    }

    public static void main(String[] arguments)
    {
        performTask(TestMethodPassing::println);
        performTask(TestMethodPassing::print);
    }
}

เอาท์พุท:

Do println
Do print

0

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

  1. ขั้นตอนที่ 1: สร้างสองอินเทอร์เฟซหนึ่งที่มีชนิดส่งคืน Java มีอินเทอร์เฟซที่คล้ายกัน แต่มีประโยชน์ในการใช้งานน้อยเพราะไม่รองรับการใช้งาน Exception Throwing


    public interface Do {
    void run() throws Exception;
    }


    public interface Return {
        R run() throws Exception;
    }
  1. ตัวอย่างวิธีที่เราใช้ทั้งสองอินเตอร์เฟสเพื่อตัดการเรียกใช้เมธอดในธุรกรรม โปรดทราบว่าเราผ่านเมธอดพร้อมพารามิเตอร์จริง


    //example - when passed method does not return any value
    public void tx(final Do func) throws Exception {
        connectionScope.beginTransaction();
        try {
            func.run();
            connectionScope.commit();
        } catch (Exception e) {
            connectionScope.rollback();
            throw e;
        } finally {
            connectionScope.close();
        }
    }

    //Invoke code above by 
    tx(() -> api.delete(6));

อีกตัวอย่างหนึ่งแสดงวิธีส่งผ่านเมธอดที่ส่งคืนบางสิ่ง



        public  R tx(final Return func) throws Exception {
    R r=null;
    connectionScope.beginTransaction();
    try {
                r=func.run();
                connectionScope.commit();
            } catch (Exception e) {
                connectionScope.rollback();
                throw e;
            } finally {
                connectionScope.close();
            }
        return r;
        }
        //Invoke code above by 
        Object x= tx(() -> api.get(id));

0

ตัวอย่างของวิธีแก้ปัญหาที่มีการสะท้อนวิธีส่งผ่านต้องเป็นสาธารณะ

import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;

public class Program {
    int i;

    public static void main(String[] args) {
        Program   obj = new Program();    //some object

        try {
            Method method = obj.getClass().getMethod("target");
            repeatMethod( 5, obj, method );
        } 
        catch ( NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            System.out.println( e ); 
        }
    }

    static void repeatMethod (int times, Object object, Method method)
        throws IllegalAccessException, InvocationTargetException {

        for (int i=0; i<times; i++)
            method.invoke(object);
    }
    public void target() {                 //public is necessary
        System.out.println("target(): "+ ++i);
    }
}

0

ฉันขอขอบคุณคำตอบข้างต้น แต่ฉันสามารถบรรลุพฤติกรรมเดียวกันโดยใช้วิธีการด้านล่าง ความคิดที่ยืมมาจากการเรียกกลับ Javascript ฉันเปิดเพื่อแก้ไขแม้ว่าจนถึงดีมาก (ในการผลิต)

แนวคิดคือใช้ชนิดส่งคืนของฟังก์ชันในลายเซ็นซึ่งหมายความว่าอัตราผลตอบแทนจะต้องคงที่

ด้านล่างเป็นฟังก์ชั่นที่เรียกใช้กระบวนการที่มีการหมดเวลา

public static void timeoutFunction(String fnReturnVal) {

    Object p = null; // whatever object you need here

    String threadSleeptime = null;

    Config config;

    try {
        config = ConfigReader.getConfigProperties();
        threadSleeptime = config.getThreadSleepTime();

    } catch (Exception e) {
        log.error(e);
        log.error("");
        log.error("Defaulting thread sleep time to 105000 miliseconds.");
        log.error("");
        threadSleeptime = "100000";
    }

    ExecutorService executor = Executors.newCachedThreadPool();
    Callable<Object> task = new Callable<Object>() {
        public Object call() {
            // Do job here using --- fnReturnVal --- and return appropriate value
            return null;
        }
    };
    Future<Object> future = executor.submit(task);

    try {
        p = future.get(Integer.parseInt(threadSleeptime), TimeUnit.MILLISECONDS);
    } catch (Exception e) {
        log.error(e + ". The function timed out after [" + threadSleeptime
                + "] miliseconds before a response was received.");
    } finally {
        // if task has started then don't stop it
        future.cancel(false);
    }
}

private static String returnString() {
    return "hello";
}

public static void main(String[] args) {
    timeoutFunction(returnString());
}
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.